Example #1
0
def test_gaussian_sum_moments():
    """Check analytical against numerical solution.
    """

    # We define three components with different flux, position and size
    F_1, F_2, F_3 = 100, 200, 300
    sigma_1, sigma_2, sigma_3 = 15, 10, 5
    x_1, x_2, x_3 = 100, 120, 70
    y_1, y_2, y_3 = 100, 90, 120

    # Convert into non-normalized amplitude for astropy model
    def A(F, sigma):
        return F * 1 / (2 * np.pi * sigma ** 2)

    # Define and evaluate models
    f_1 = Gaussian2D(A(F_1, sigma_1), x_1, y_1, sigma_1, sigma_1)
    f_2 = Gaussian2D(A(F_2, sigma_2), x_2, y_2, sigma_2, sigma_2)
    f_3 = Gaussian2D(A(F_3, sigma_3), x_3, y_3, sigma_3, sigma_3)

    F_1_image = discretize_model(f_1, (0, 200), (0, 200))
    F_2_image = discretize_model(f_2, (0, 200), (0, 200))
    F_3_image = discretize_model(f_3, (0, 200), (0, 200))

    moments_num = measure_image_moments(F_1_image + F_2_image + F_3_image)

    # Compute analytical values
    cov_matrix = np.zeros((12, 12))
    F = [F_1, F_2, F_3]
    sigma = [sigma_1, sigma_2, sigma_3]
    x = [x_1, x_2, x_3]
    y = [y_1, y_2, y_3]

    moments_ana, uncertainties = gaussian_sum_moments(F, sigma, x, y, cov_matrix)
    assert_allclose(moments_ana, moments_num, 1e-6)
    assert_allclose(uncertainties, 0)
Example #2
0
 def _make_gaussian_sources_image(self, table_bgstars, nsigma=6):
     "the one from photutils was too slow"
     image = np.zeros(self.image_shape_pix)
     for i in range(self.nstars_in_image):
         amplitude = table_bgstars['flux'][i] / (
             2. * np.pi * table_bgstars['x_stddev'][i] *
             table_bgstars['y_stddev'][i])
         x_mean = table_bgstars['x_mean'][i]
         y_mean = table_bgstars['y_mean'][i]
         x_stddev = table_bgstars['x_stddev'][i]
         y_stddev = table_bgstars['y_stddev'][i]
         gmodel = Gaussian2D(amplitude=amplitude,
                             x_mean=0,
                             y_mean=0,
                             x_stddev=x_stddev,
                             y_stddev=y_stddev,
                             theta=table_bgstars['theta'][i])
         x_range = self._get_range(mean_val=x_mean,
                                   sigma=x_stddev,
                                   nsigma=nsigma,
                                   axis='x')
         y_range = self._get_range(mean_val=y_mean,
                                   sigma=y_stddev,
                                   nsigma=nsigma,
                                   axis='y')
         dmodel = discretize_model(model=gmodel,
                                   x_range=tuple(x_range - x_mean),
                                   y_range=tuple(y_range - y_mean),
                                   mode='oversample',
                                   factor=self.psf_oversample)
         image[int(x_range[0]):int(x_range[1]),
               int(y_range[0]):int(y_range[1])] += dmodel.T
     return image
Example #3
0
def exp_kern(alpha, size, norm=True, mode='center', factor=10):
    """
    Generate 2D, radially symmetric exponential kernel The kernels are 
    discretized using astropy.convolution.discretize_model.

    Parameters
    ----------
    alpha : float
        The scale length of the exponential.
    size : odd int
        Number of pixel in x & y directions.
    norm_array : bool, optional
        If True, normalize the kern array. 
    mode : str, optional
        One of the following discretization modes: 
        'center', 'oversample', 'linear_interp', 
        or 'integrate'. See astropy docs for details. 
    factor : float or int
        Factor of oversampling. 
    Returns
    -------
    kern : 2D ndarray
        The convolution kernel. 
    """
    assert size % 2 != 0, 'ERROR: size must be odd'
    x_range = (-(int(size) - 1) // 2, (int(size) - 1) // 2 + 1)
    model = lambda x, y: np.exp(-np.sqrt(x**2 + y**2) / alpha)
    kern = discretize_model(model, x_range, x_range, mode=mode, factor=factor)
    if norm:
        kern /= kern.sum()
    return kern
Example #4
0
 def _discretized_psfmodel(self, dx, dy):
     minx = np.min(dx)
     miny = np.min(dy)
     maxx = np.max(dx) + 0.5
     maxy = np.max(dy) + 0.5
     out =  discretize_model(self.psfmodel, (minx, maxx), (miny, maxy), self.discretizemode, **self._discretize_modelkwargs)
     return out
Example #5
0
def test_gaussian_sum_moments():
    """Check analytical against numerical solution.
    """
    binsz = 0.02

    # We define three components with different flux, position and size in pixel coordinates
    F_1, F_2, F_3 = 100, 200, 300
    sigma_1, sigma_2, sigma_3 = 15, 10, 5
    x_1, x_2, x_3 = 100, 120, 70
    y_1, y_2, y_3 = 100, 90, 120

    # Convert into non-normalized amplitude for astropy model
    def A(F, sigma):
        return F * 1 / (2 * np.pi * sigma**2)

    # Define and evaluate models
    f_1 = Gaussian2D(A(F_1, sigma_1), x_1, y_1, sigma_1, sigma_1)
    f_2 = Gaussian2D(A(F_2, sigma_2), x_2, y_2, sigma_2, sigma_2)
    f_3 = Gaussian2D(A(F_3, sigma_3), x_3, y_3, sigma_3, sigma_3)

    F_1_image = discretize_model(f_1, (0, 201), (0, 201))
    F_2_image = discretize_model(f_2, (0, 201), (0, 201))
    F_3_image = discretize_model(f_3, (0, 201), (0, 201))

    image = WcsNDMap.create(npix=(201, 201), binsz=binsz)
    image.data = F_1_image + F_2_image + F_3_image
    moments_num = measure_image_moments(image)

    # Right now the flux doesn't have a unit
    moments_num = [moments_num[0]] + [_.value for _ in moments_num[1:]]

    # Compute analytical values
    cov_matrix = np.zeros((12, 12))
    F = [F_1, F_2, F_3]
    sigma = np.array([sigma_1, sigma_2, sigma_3]) * binsz
    x, y = image.geom.wcs.wcs_pix2world([x_1, x_2, x_3], [y_1, y_2, y_3], 0)
    x = np.where(x > 180, x - 360, x)

    moments_ana, uncertainties = gaussian_sum_moments(F,
                                                      sigma,
                                                      x,
                                                      y,
                                                      cov_matrix,
                                                      shift=0)
    assert_allclose(moments_ana, moments_num, 1e-6)
    assert_allclose(uncertainties, 0)
Example #6
0
def test_gaussian_sum_moments():
    """Check analytical against numerical solution.
    """
    # We define three components with different flux, position and size in pixel coordinates
    F_1, F_2, F_3 = 100, 200, 300
    sigma_1, sigma_2, sigma_3 = 15, 10, 5
    x_1, x_2, x_3 = 100, 120, 70
    y_1, y_2, y_3 = 100, 90, 120

    # Convert into non-normalized amplitude for astropy model
    def A(F, sigma):
        return F * 1 / (2 * np.pi * sigma**2)

    # Define and evaluate models
    f_1 = Gaussian2D(A(F_1, sigma_1), x_1, y_1, sigma_1, sigma_1)
    f_2 = Gaussian2D(A(F_2, sigma_2), x_2, y_2, sigma_2, sigma_2)
    f_3 = Gaussian2D(A(F_3, sigma_3), x_3, y_3, sigma_3, sigma_3)

    F_1_image = discretize_model(f_1, (0, 201), (0, 201))
    F_2_image = discretize_model(f_2, (0, 201), (0, 201))
    F_3_image = discretize_model(f_3, (0, 201), (0, 201))
    image = set_header(fits.ImageHDU(F_1_image + F_2_image + F_3_image))
    moments_num = measure_image_moments(image)

    wcs = WCS(image.header)

    # Compute analytical values
    cov_matrix = np.zeros((12, 12))
    F = [F_1, F_2, F_3]
    sigma = np.array([sigma_1, sigma_2, sigma_3]) * BINSZ
    origin = 0  # convention for gammapy
    x, y = wcs.wcs_pix2world([x_1, x_2, x_3], [y_1, y_2, y_3], origin)

    # Fix longitude range, is there a wcs option for this?
    x = np.where(x > 180, x - 360, x)

    moments_ana, uncertainties = gaussian_sum_moments(F,
                                                      sigma,
                                                      x,
                                                      y,
                                                      cov_matrix,
                                                      shift=0)
    assert_allclose(moments_ana, moments_num, 1e-6)
    assert_allclose(uncertainties, 0)
Example #7
0
def gauss_kern(width, size, width_type='fwhm', norm_array=False, mode='center', 
               factor=10):
    """
    Generate a 2D radially symmetric Gaussian kernel for sextractor,
    The kernels are discretized using `astropy.convolution.discretize_model`.

    Parameters
    ----------
    width : float
        The width of the Gaussian. 
    size : odd int
        Number of pixel in x & y directions.
    width_type : string, optional
        The width type given ('fwhm' or 'sigma').
    norm_array : bool, optional
        If True, normalize the kern array. 
    mode : str, optional
        One of the following discretization modes: 
        'center', 'oversample', 'linear_interp', 
        or 'integrate'. See astropy docs for details. 
    factor : float or int
        Factor of oversampling. 

    Returns
    -------
    kern : 2D ndarray, if (return_fn=False)
        The convolution kernel. 
    kern, fn, comment : ndarray, string, string (if return_fn=True)
        The kernel, file name, and the comment
        for the file.

    Notes
    -----
     i) The kernel will be normalized by sextractor by 
        default, and the non-normalized files are a bit 
        cleaner due to less leading zeros. 
    """
    assert size%2!=0, 'ERROR: size must be odd'
    from astropy.convolution import discretize_model

    convert = 2.0*np.sqrt(2*np.log(2))
    if width_type=='fwhm':
        sigma = width/convert
        fwhm = width
    elif width_type=='sigma':
        sigma = width
        fwhm = width*convert

    x_range = (-(int(size) - 1) // 2, (int(size) - 1) // 2 + 1)
    model = lambda x, y: np.exp(-(x**2 + y**2)/(2*sigma**2))
    kern = discretize_model(model, x_range, x_range, mode=mode, factor=factor)

    if norm_array:
        kern /= kern.sum()

    return kern
Example #8
0
def test_gaussian_sum_moments():
    """Check analytical against numerical solution.
    """
    binsz = 0.02

    # We define three components with different flux, position and size in pixel coordinates
    F_1, F_2, F_3 = 100, 200, 300
    sigma_1, sigma_2, sigma_3 = 15, 10, 5
    x_1, x_2, x_3 = 100, 120, 70
    y_1, y_2, y_3 = 100, 90, 120

    # Convert into non-normalized amplitude for astropy model
    def A(F, sigma):
        return F * 1 / (2 * np.pi * sigma ** 2)

    # Define and evaluate models
    f_1 = Gaussian2D(A(F_1, sigma_1), x_1, y_1, sigma_1, sigma_1)
    f_2 = Gaussian2D(A(F_2, sigma_2), x_2, y_2, sigma_2, sigma_2)
    f_3 = Gaussian2D(A(F_3, sigma_3), x_3, y_3, sigma_3, sigma_3)

    F_1_image = discretize_model(f_1, (0, 201), (0, 201))
    F_2_image = discretize_model(f_2, (0, 201), (0, 201))
    F_3_image = discretize_model(f_3, (0, 201), (0, 201))

    image = WcsNDMap.create(npix=(201, 201), binsz=binsz)
    image.data = F_1_image + F_2_image + F_3_image
    moments_num = measure_image_moments(image)

    # Right now the flux doesn't have a unit
    moments_num = [moments_num[0]] + [_.value for _ in moments_num[1:]]

    # Compute analytical values
    cov_matrix = np.zeros((12, 12))
    F = [F_1, F_2, F_3]
    sigma = np.array([sigma_1, sigma_2, sigma_3]) * binsz
    x, y = image.geom.wcs.wcs_pix2world([x_1, x_2, x_3], [y_1, y_2, y_3], 0)
    x = np.where(x > 180, x - 360, x)

    moments_ana, uncertainties = gaussian_sum_moments(
        F, sigma, x, y, cov_matrix, shift=0
    )
    assert_allclose(moments_ana, moments_num, 1e-6)
    assert_allclose(uncertainties, 0)
Example #9
0
def test_gaussian_sum_moments():
    """Check analytical against numerical solution.
    """
    # We define three components with different flux, position and size in pixel coordinates
    F_1, F_2, F_3 = 100, 200, 300
    sigma_1, sigma_2, sigma_3 = 15, 10, 5
    x_1, x_2, x_3 = 100, 120, 70
    y_1, y_2, y_3 = 100, 90, 120

    # Convert into non-normalized amplitude for astropy model
    def A(F, sigma):
        return F * 1 / (2 * np.pi * sigma ** 2)

    # Define and evaluate models
    f_1 = Gaussian2D(A(F_1, sigma_1), x_1, y_1, sigma_1, sigma_1)
    f_2 = Gaussian2D(A(F_2, sigma_2), x_2, y_2, sigma_2, sigma_2)
    f_3 = Gaussian2D(A(F_3, sigma_3), x_3, y_3, sigma_3, sigma_3)

    F_1_image = discretize_model(f_1, (0, 201), (0, 201))
    F_2_image = discretize_model(f_2, (0, 201), (0, 201))
    F_3_image = discretize_model(f_3, (0, 201), (0, 201))
    image = set_header(fits.ImageHDU(F_1_image + F_2_image + F_3_image))
    moments_num = measure_image_moments(image)

    wcs = WCS(image.header)

    # Compute analytical values
    cov_matrix = np.zeros((12, 12))
    F = [F_1, F_2, F_3]
    sigma = np.array([sigma_1, sigma_2, sigma_3]) * BINSZ
    origin = 0  # convention for gammapy
    x, y = wcs.wcs_pix2world([x_1, x_2, x_3], [y_1, y_2, y_3], origin)

    # Fix longitude range, is there a wcs option for this?
    x = np.where(x > 180, x - 360, x)

    moments_ana, uncertainties = gaussian_sum_moments(F, sigma, x, y, cov_matrix, shift=0)
    assert_allclose(moments_ana, moments_num, 1e-6)
    assert_allclose(uncertainties, 0)
Example #10
0
def exp_kern(alpha, size, norm_array=False, mode='center', factor=10):
    """
    Generate 2D, radially symmetric exponential kernal for sextractor. 
    The kernels are discretized using `astropy.convolution.discretize_model`.

    Parameters
    ----------
    alpha : float
        The scale length of the exponential.
    size : odd int
        Number of pixel in x & y directions.
    norm_array : bool, optional
        If True, normalize the kern array. 
    mode : str, optional
        One of the following discretization modes: 
        'center', 'oversample', 'linear_interp', 
        or 'integrate'. See astropy docs for details. 
    factor : float or int
        Factor of oversampling. 

    Returns
    -------
    kern : 2D ndarray, if (return_fn=False)
        The convolution kernel. 
    kern, fn, comment : ndarray, string, string (if return_fn=True)
        The kernel, file name, and the comment
        for the file.

    Notes
    -----
    The kernel will be normalized by sextractor by 
    default, and the non-normalized files are a bit 
    cleaner due to less leading zeros. 
    """
    assert size%2!=0, 'ERROR: size must be odd'
    from astropy.convolution import discretize_model

    x_range = (-(int(size) - 1) // 2, (int(size) - 1) // 2 + 1)
    model = lambda x, y: np.exp(-np.sqrt(x**2 + y**2)/alpha)
    kern = discretize_model(model, x_range, x_range, mode=mode, factor=factor)

    if norm_array:
        kern /= kern.sum()
    
    return kern
Example #11
0
    def pixellated(self, size=21, offsets=(0.0, 0.0)):
        """
        Calculates a pixellated version of the PSF.

        The pixel values are calculated using 10x oversampling, i.e. by
        evaluating the continuous PSF model at a 10 x 10 grid of positions
        within each pixel and averaging the results.

        Parameters
        ----------
        size : int, optional
            Size of the pixellated PSF to calculate, the returned image
            will have `size` x `size` pixels. Default value 21.
        offset : tuple of floats, optional
            y and x axis offsets of the centre of the PSF from the centre
            of the returned image, in pixels.

        Returns
        -------
        pixellated : numpy.array
            Pixellated PSF image with `size` by `size` pixels. The PSF
            is normalised to an integral of 1 however the sum of the
            pixellated PSF will be somewhat less due to truncation of the
            PSF wings by the edge of the image.
        """
        size = int(size)
        if size <= 0:
            raise ValueError("`size` must be > 0, got {}!".format(size))

        # Update PSF centre coordinates
        self.x_0 = offsets[0]
        self.y_0 = offsets[1]

        xrange = (-(size - 1) / 2, (size + 1) / 2)
        yrange = (-(size - 1) / 2, (size + 1) / 2)

        return discretize_model(self,
                                xrange,
                                yrange,
                                mode='oversample',
                                factor=10)
Example #12
0
 def make_gaussian_source_image(self, image, amplitude, x_mean, y_mean,
                                x_stddev, y_stddev, theta, nsigma):
     gmodel = Gaussian2D(amplitude=amplitude,
                         x_mean=0,
                         y_mean=0,
                         x_stddev=x_stddev,
                         y_stddev=y_stddev,
                         theta=theta)
     x_range = self._get_range(mean_val=x_mean,
                               sigma=x_stddev,
                               nsigma=nsigma,
                               axis='x')
     y_range = self._get_range(mean_val=y_mean,
                               sigma=y_stddev,
                               nsigma=nsigma,
                               axis='y')
     dmodel = discretize_model(model=gmodel,
                               x_range=tuple(x_range - x_mean),
                               y_range=tuple(y_range - y_mean),
                               mode='oversample',
                               factor=self.psf_oversample)
     image[int(x_range[0]):int(x_range[1]),
           int(y_range[0]):int(y_range[1])] += dmodel.T
     return image
Example #13
0
def make_model_sources_image(shape, model, source_table, oversample=1):
    """
    Make an image containing sources generated from a user-specified
    model.

    Parameters
    ----------
    shape : 2-tuple of int
        The shape of the output 2D image.

    model : 2D astropy.modeling.models object
        The model to be used for rendering the sources.

    source_table : `~astropy.table.Table`
        Table of parameters for the sources.  Each row of the table
        corresponds to a source whose model parameters are defined by
        the column names, which must match the model parameter names.
        Column names that do not match model parameters will be ignored.
        Model parameters not defined in the table will be set to the
        ``model`` default value.

    oversample : float, optional
        The sampling factor used to discretize the models on a pixel
        grid.  If the value is 1.0 (the default), then the models will
        be discretized by taking the value at the center of the pixel
        bin.  Note that this method will not preserve the total flux of
        very small sources.  Otherwise, the models will be discretized
        by taking the average over an oversampled grid.  The pixels will
        be oversampled by the ``oversample`` factor.

    Returns
    -------
    image : 2D `~numpy.ndarray`
        Image containing model sources.

    See Also
    --------
    make_random_models_table, make_gaussian_sources_image

    Examples
    --------
    .. plot::
        :include-source:

        from collections import OrderedDict
        from astropy.modeling.models import Moffat2D
        from photutils.datasets import (make_random_models_table,
                                        make_model_sources_image)

        model = Moffat2D()
        n_sources = 10
        shape = (100, 100)
        param_ranges = [('amplitude', [100, 200]),
                        ('x_0', [0, shape[1]]),
                        ('y_0', [0, shape[0]]),
                        ('gamma', [5, 10]),
                        ('alpha', [1, 2])]
        param_ranges = OrderedDict(param_ranges)
        sources = make_random_models_table(n_sources, param_ranges,
                                           random_state=12345)

        data = make_model_sources_image(shape, model, sources)
        plt.imshow(data)
    """

    image = np.zeros(shape, dtype=np.float64)
    y, x = np.indices(shape)

    params_to_set = []
    for param in source_table.colnames:
        if param in model.param_names:
            params_to_set.append(param)

    # Save the initial parameter values so we can set them back when
    # done with the loop.  It's best not to copy a model, because some
    # models (e.g. PSF models) may have substantial amounts of data in
    # them.
    init_params = {param: getattr(model, param) for param in params_to_set}

    try:
        for i, source in enumerate(source_table):
            for param in params_to_set:
                setattr(model, param, source[param])

            if oversample == 1:
                image += model(x, y)
            else:
                image += discretize_model(model, (0, shape[1]),
                                          (0, shape[0]), mode='oversample',
                                          factor=oversample)
    finally:
        for param, value in init_params.items():
            setattr(model, param, value)

    return image
Example #14
0
def make_gaussian_sources(image_shape, source_table, oversample=1):
    """
    Make an image containing 2D Gaussian sources.

    Parameters
    ----------
    image_shape : 2-tuple of int
        Shape of the output 2D image.

    source_table : `astropy.table.Table`
        Table of parameters for the Gaussian sources.  Each row of the
        table corresponds to a Gaussian source whose parameters are
        defined by the column names.  The column names must include
        ``flux`` or ``amplitude``, ``x_mean``, ``y_mean``, ``x_stddev``,
        ``y_stddev``, and ``theta`` (see
        `~astropy.modeling.functional_models.Gaussian2D` for a
        description of most of these parameter names).  If both ``flux``
        and ``amplitude`` are present, then ``amplitude`` will be
        ignored.

    oversample : float, optional
        The sampling factor used to discretize the
        `~astropy.modeling.functional_models.Gaussian2D` models on a
        pixel grid.

        If the value is 1.0 (the default), then the models will be
        discretized by taking the value at the center of the pixel bin.
        Note that this method will not preserve the total flux of very
        small sources.

        Otherwise, the models will be discretized by taking the average
        over an oversampled grid.  The pixels will be oversampled by the
        ``oversample`` factor.

    Returns
    -------
    image : `numpy.ndarray`
        Image containing 2D Gaussian sources.

    See Also
    --------
    make_random_gaussians, make_noise_image, make_poisson_noise

    Examples
    --------

    .. plot::
        :include-source:

        # make a table of Gaussian sources
        from astropy.table import Table
        table = Table()
        table['amplitude'] = [50, 70, 150, 210]
        table['x_mean'] = [160, 25, 150, 90]
        table['y_mean'] = [70, 40, 25, 60]
        table['x_stddev'] = [15.2, 5.1, 3., 8.1]
        table['y_stddev'] = [2.6, 2.5, 3., 4.7]
        table['theta'] = np.array([145., 20., 0., 60.]) * np.pi / 180.

        # make an image of the sources without noise, with Gaussian
        # noise, and with Poisson noise
        from photutils.datasets import make_gaussian_sources
        from photutils.datasets import make_noise_image
        shape = (100, 200)
        image1 = make_gaussian_sources(shape, table)
        image2 = image1 + make_noise_image(shape, type='gaussian', mean=0.,
                                           stddev=5.)
        image3 = image1 + make_noise_image(shape, type='poisson', mean=5.)

        # plot the images
        import matplotlib.pyplot as plt
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 12))
        ax1.imshow(image1, origin='lower', interpolation='nearest')
        ax2.imshow(image2, origin='lower', interpolation='nearest')
        ax3.imshow(image3, origin='lower', interpolation='nearest')
    """

    image = np.zeros(image_shape, dtype=np.float64)
    y, x = np.indices(image_shape)

    if 'flux' in source_table.colnames:
        amplitude = source_table['flux'] / (
            2. * np.pi * source_table['x_stddev'] * source_table['y_stddev'])
    elif 'amplitude' in source_table.colnames:
        amplitude = source_table['amplitude']
    else:
        raise ValueError('either "amplitude" or "flux" must be columns in '
                         'the input source_table')

    for i, source in enumerate(source_table):
        model = Gaussian2D(amplitude=amplitude[i],
                           x_mean=source['x_mean'],
                           y_mean=source['y_mean'],
                           x_stddev=source['x_stddev'],
                           y_stddev=source['y_stddev'],
                           theta=source['theta'])
        if oversample == 1:
            image += model(x, y)
        else:
            image += discretize_model(model, (0, image_shape[1]),
                                      (0, image_shape[0]),
                                      mode='oversample',
                                      factor=oversample)
    return image
Example #15
0
def make_gaussian_sources(image_shape, source_table, oversample=1):
    """
    Make an image containing 2D Gaussian sources.

    Parameters
    ----------
    image_shape : 2-tuple of int
        Shape of the output 2D image.

    source_table : `astropy.table.Table`
        Table of parameters for the Gaussian sources.  Each row of the
        table corresponds to a Gaussian source whose parameters are
        defined by the column names.  The column names must include
        ``flux`` or ``amplitude``, ``x_mean``, ``y_mean``, ``x_stddev``,
        ``y_stddev``, and ``theta`` (see
        `~astropy.modeling.functional_models.Gaussian2D` for a
        description of most of these parameter names).  If both ``flux``
        and ``amplitude`` are present, then ``amplitude`` will be
        ignored.

    oversample : float, optional
        The sampling factor used to discretize the
        `~astropy.modeling.functional_models.Gaussian2D` models on a
        pixel grid.

        If the value is 1.0 (the default), then the models will be
        discretized by taking the value at the center of the pixel bin.
        Note that this method will not preserve the total flux of very
        small sources.

        Otherwise, the models will be discretized by taking the average
        over an oversampled grid.  The pixels will be oversampled by the
        ``oversample`` factor.

    Returns
    -------
    image : `numpy.ndarray`
        Image containing 2D Gaussian sources.

    See Also
    --------
    make_random_gaussians, make_noise_image, make_poisson_noise

    Examples
    --------

    .. plot::
        :include-source:

        # make a table of Gaussian sources
        from astropy.table import Table
        table = Table()
        table['amplitude'] = [50, 70, 150, 210]
        table['x_mean'] = [160, 25, 150, 90]
        table['y_mean'] = [70, 40, 25, 60]
        table['x_stddev'] = [15.2, 5.1, 3., 8.1]
        table['y_stddev'] = [2.6, 2.5, 3., 4.7]
        table['theta'] = np.array([145., 20., 0., 60.]) * np.pi / 180.

        # make an image of the sources without noise, with Gaussian
        # noise, and with Poisson noise
        from photutils.datasets import make_gaussian_sources
        from photutils.datasets import make_noise_image
        shape = (100, 200)
        image1 = make_gaussian_sources(shape, table)
        image2 = image1 + make_noise_image(shape, type='gaussian', mean=0.,
                                           stddev=5.)
        image3 = image1 + make_noise_image(shape, type='poisson', mean=5.)

        # plot the images
        import matplotlib.pyplot as plt
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 12))
        ax1.imshow(image1, origin='lower', interpolation='nearest')
        ax2.imshow(image2, origin='lower', interpolation='nearest')
        ax3.imshow(image3, origin='lower', interpolation='nearest')
    """

    image = np.zeros(image_shape, dtype=np.float64)
    y, x = np.indices(image_shape)

    if 'flux' in source_table.colnames:
        amplitude = source_table['flux'] / (2. * np.pi *
                                            source_table['x_stddev'] *
                                            source_table['y_stddev'])
    elif 'amplitude' in source_table.colnames:
        amplitude = source_table['amplitude']
    else:
        raise ValueError('either "amplitude" or "flux" must be columns in '
                         'the input source_table')

    for i, source in enumerate(source_table):
        model = Gaussian2D(amplitude=amplitude[i], x_mean=source['x_mean'],
                           y_mean=source['y_mean'],
                           x_stddev=source['x_stddev'],
                           y_stddev=source['y_stddev'], theta=source['theta'])
        if oversample == 1:
            image += model(x, y)
        else:
            image += discretize_model(model, (0, image_shape[1]),
                                      (0, image_shape[0]), mode='oversample',
                                      factor=oversample)
    return image
Example #16
0
def make_gaussian_sources(image_shape, source_table, oversample=1, unit=None,
                          hdu=False, wcs=False, wcsheader=None):
    """
    Make an image containing 2D Gaussian sources.

    Parameters
    ----------
    image_shape : 2-tuple of int
        Shape of the output 2D image.

    source_table : `~astropy.table.Table`
        Table of parameters for the Gaussian sources.  Each row of the
        table corresponds to a Gaussian source whose parameters are
        defined by the column names.  The column names must include
        ``flux`` or ``amplitude``, ``x_mean``, ``y_mean``, ``x_stddev``,
        ``y_stddev``, and ``theta`` (see
        `~astropy.modeling.functional_models.Gaussian2D` for a
        description of most of these parameter names).  If both ``flux``
        and ``amplitude`` are present, then ``amplitude`` will be
        ignored.

    oversample : float, optional
        The sampling factor used to discretize the
        `~astropy.modeling.functional_models.Gaussian2D` models on a
        pixel grid.

        If the value is 1.0 (the default), then the models will be
        discretized by taking the value at the center of the pixel bin.
        Note that this method will not preserve the total flux of very
        small sources.

        Otherwise, the models will be discretized by taking the average
        over an oversampled grid.  The pixels will be oversampled by the
        ``oversample`` factor.

    unit : `~astropy.units.UnitBase` instance, str, optional
        An object that represents the unit desired for the output image.
        Must be an `~astropy.units.UnitBase` object or a string
        parseable by the `~astropy.units` package.

    hdu : bool, optional
        If `True` returns ``image`` as an `~astropy.io.fits.ImageHDU`
        object.  To include WCS information in the header, use the
        ``wcs`` and ``wcsheader`` inputs.  Otherwise the header will be
        minimal.  Default is `False`.

    wcs : bool, optional
        If `True` and ``hdu=True``, then a simple WCS will be included
        in the returned `~astropy.io.fits.ImageHDU` header.  Default is
        `False`.

    wcsheader : dict or `None`, optional
        If ``hdu`` and ``wcs`` are `True`, this dictionary is passed to
        `~astropy.wcs.WCS` to generate the returned
        `~astropy.io.fits.ImageHDU` header.

    Returns
    -------
    image : `~numpy.ndarray` or `~astropy.units.Quantity` or `~astropy.io.fits.ImageHDU`
        Image or `~astropy.io.fits.ImageHDU` containing 2D Gaussian
        sources.

    See Also
    --------
    make_random_gaussians, make_noise_image, make_poisson_noise

    Examples
    --------

    .. plot::
        :include-source:

        # make a table of Gaussian sources
        from astropy.table import Table
        table = Table()
        table['amplitude'] = [50, 70, 150, 210]
        table['x_mean'] = [160, 25, 150, 90]
        table['y_mean'] = [70, 40, 25, 60]
        table['x_stddev'] = [15.2, 5.1, 3., 8.1]
        table['y_stddev'] = [2.6, 2.5, 3., 4.7]
        table['theta'] = np.array([145., 20., 0., 60.]) * np.pi / 180.

        # make an image of the sources without noise, with Gaussian
        # noise, and with Poisson noise
        from photutils.datasets import make_gaussian_sources
        from photutils.datasets import make_noise_image
        shape = (100, 200)
        image1 = make_gaussian_sources(shape, table)
        image2 = image1 + make_noise_image(shape, type='gaussian', mean=5.,
                                           stddev=5.)
        image3 = image1 + make_noise_image(shape, type='poisson', mean=5.)

        # plot the images
        import matplotlib.pyplot as plt
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 12))
        ax1.imshow(image1, origin='lower', interpolation='nearest')
        ax2.imshow(image2, origin='lower', interpolation='nearest')
        ax3.imshow(image3, origin='lower', interpolation='nearest')
    """

    image = np.zeros(image_shape, dtype=np.float64)
    y, x = np.indices(image_shape)

    if 'flux' in source_table.colnames:
        amplitude = source_table['flux'] / (2. * np.pi *
                                            source_table['x_stddev'] *
                                            source_table['y_stddev'])
    elif 'amplitude' in source_table.colnames:
        amplitude = source_table['amplitude']
    else:
        raise ValueError('either "amplitude" or "flux" must be columns in '
                         'the input source_table')

    for i, source in enumerate(source_table):
        model = Gaussian2D(amplitude=amplitude[i], x_mean=source['x_mean'],
                           y_mean=source['y_mean'],
                           x_stddev=source['x_stddev'],
                           y_stddev=source['y_stddev'], theta=source['theta'])
        if oversample == 1:
            image += model(x, y)
        else:
            image += discretize_model(model, (0, image_shape[1]),
                                      (0, image_shape[0]), mode='oversample',
                                      factor=oversample)
    if unit is not None:
        image = u.Quantity(image, unit=unit)

    if wcs and not hdu:
        raise ValueError("wcs header only works with hdu output, use keyword "
                         "'hdu=True'")

    if hdu is True:
        from astropy.io import fits
        if wcs:
            from astropy.wcs import WCS
            if wcsheader is None:
                # Go with the simplest valid header
                header = WCS({'CTYPE1': 'RA---TAN',
                              'CTYPE2': 'DEC--TAN',
                              'CRPIX1': int(image_shape[1] / 2),
                              'CRPIX2': int(image_shape[0] / 2)}, ).to_header()
            else:
                header = WCS(wcsheader)
        else:
            header = None
        image = fits.ImageHDU(image, header=header)

    return image
Example #17
0
def make_model_sources_image(shape, model, source_table, oversample=1):
    """
    Make an image containing sources generated from a user-specified
    model.

    Parameters
    ----------
    shape : 2-tuple of int
        The shape of the output 2D image.

    model : 2D astropy.modeling.models object
        The model to be used for rendering the sources.

    source_table : `~astropy.table.Table`
        Table of parameters for the sources.  Each row of the table
        corresponds to a source whose model parameters are defined by
        the column names, which must match the model parameter names.
        Column names that do not match model parameters will be ignored.
        Model parameters not defined in the table will be set to the
        ``model`` default value.

    oversample : float, optional
        The sampling factor used to discretize the models on a pixel
        grid.  If the value is 1.0 (the default), then the models will
        be discretized by taking the value at the center of the pixel
        bin.  Note that this method will not preserve the total flux of
        very small sources.  Otherwise, the models will be discretized
        by taking the average over an oversampled grid.  The pixels will
        be oversampled by the ``oversample`` factor.

    Returns
    -------
    image : 2D `~numpy.ndarray`
        Image containing model sources.

    See Also
    --------
    make_random_models_table, make_gaussian_sources_image

    Examples
    --------
    .. plot::
        :include-source:

        from collections import OrderedDict
        from astropy.modeling.models import Moffat2D
        from photutils.datasets import (make_random_models_table,
                                        make_model_sources_image)

        model = Moffat2D()
        n_sources = 10
        shape = (100, 100)
        param_ranges = [('amplitude', [100, 200]),
                        ('x_0', [0, shape[1]]),
                        ('y_0', [0, shape[0]]),
                        ('gamma', [5, 10]),
                        ('alpha', [1, 2])]
        param_ranges = OrderedDict(param_ranges)
        sources = make_random_models_table(n_sources, param_ranges,
                                           random_state=12345)

        data = make_model_sources_image(shape, model, sources)
        plt.imshow(data)
    """

    image = np.zeros(shape, dtype=np.float64)
    y, x = np.indices(shape)

    params_to_set = []
    for param in source_table.colnames:
        if param in model.param_names:
            params_to_set.append(param)

    # Save the initial parameter values so we can set them back when
    # done with the loop.  It's best not to copy a model, because some
    # models (e.g. PSF models) may have substantial amounts of data in
    # them.
    init_params = {param: getattr(model, param) for param in params_to_set}

    try:
        for i, source in enumerate(source_table):
            for param in params_to_set:
                setattr(model, param, source[param])

            if oversample == 1:
                image += model(x, y)
            else:
                image += discretize_model(model, (0, shape[1]), (0, shape[0]),
                                          mode='oversample',
                                          factor=oversample)
    finally:
        for param, value in init_params.items():
            setattr(model, param, value)

    return image
Example #18
0
def make_gaussian_sources(image_shape,
                          source_table,
                          oversample=1,
                          unit=None,
                          hdu=False,
                          wcs=False,
                          wcsheader=None):
    """
    Make an image containing 2D Gaussian sources.

    Parameters
    ----------
    image_shape : 2-tuple of int
        Shape of the output 2D image.

    source_table : `~astropy.table.Table`
        Table of parameters for the Gaussian sources.  Each row of the
        table corresponds to a Gaussian source whose parameters are
        defined by the column names.  The column names must include
        ``flux`` or ``amplitude``, ``x_mean``, ``y_mean``, ``x_stddev``,
        ``y_stddev``, and ``theta`` (see
        `~astropy.modeling.functional_models.Gaussian2D` for a
        description of most of these parameter names).  If both ``flux``
        and ``amplitude`` are present, then ``amplitude`` will be
        ignored.

    oversample : float, optional
        The sampling factor used to discretize the
        `~astropy.modeling.functional_models.Gaussian2D` models on a
        pixel grid.

        If the value is 1.0 (the default), then the models will be
        discretized by taking the value at the center of the pixel bin.
        Note that this method will not preserve the total flux of very
        small sources.

        Otherwise, the models will be discretized by taking the average
        over an oversampled grid.  The pixels will be oversampled by the
        ``oversample`` factor.

    unit : `~astropy.units.UnitBase` instance, str, optional
        An object that represents the unit desired for the output image.
        Must be an `~astropy.units.UnitBase` object or a string
        parseable by the `~astropy.units` package.

    hdu : bool, optional
        If `True` returns ``image`` as an `~astropy.io.fits.ImageHDU`
        object.  To include WCS information in the header, use the
        ``wcs`` and ``wcsheader`` inputs.  Otherwise the header will be
        minimal.  Default is `False`.

    wcs : bool, optional
        If `True` and ``hdu=True``, then a simple WCS will be included
        in the returned `~astropy.io.fits.ImageHDU` header.  Default is
        `False`.

    wcsheader : dict or `None`, optional
        If ``hdu`` and ``wcs`` are `True`, this dictionary is passed to
        `~astropy.wcs.WCS` to generate the returned
        `~astropy.io.fits.ImageHDU` header.

    Returns
    -------
    image : `~numpy.ndarray` or `~astropy.units.Quantity` or `~astropy.io.fits.ImageHDU`
        Image or `~astropy.io.fits.ImageHDU` containing 2D Gaussian
        sources.

    See Also
    --------
    make_random_gaussians, make_noise_image, make_poisson_noise

    Examples
    --------

    .. plot::
        :include-source:

        # make a table of Gaussian sources
        from astropy.table import Table
        table = Table()
        table['amplitude'] = [50, 70, 150, 210]
        table['x_mean'] = [160, 25, 150, 90]
        table['y_mean'] = [70, 40, 25, 60]
        table['x_stddev'] = [15.2, 5.1, 3., 8.1]
        table['y_stddev'] = [2.6, 2.5, 3., 4.7]
        table['theta'] = np.array([145., 20., 0., 60.]) * np.pi / 180.

        # make an image of the sources without noise, with Gaussian
        # noise, and with Poisson noise
        from photutils.datasets import make_gaussian_sources
        from photutils.datasets import make_noise_image
        shape = (100, 200)
        image1 = make_gaussian_sources(shape, table)
        image2 = image1 + make_noise_image(shape, type='gaussian', mean=5.,
                                           stddev=5.)
        image3 = image1 + make_noise_image(shape, type='poisson', mean=5.)

        # plot the images
        import matplotlib.pyplot as plt
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 12))
        ax1.imshow(image1, origin='lower', interpolation='nearest')
        ax2.imshow(image2, origin='lower', interpolation='nearest')
        ax3.imshow(image3, origin='lower', interpolation='nearest')
    """

    image = np.zeros(image_shape, dtype=np.float64)
    y, x = np.indices(image_shape)

    if 'flux' in source_table.colnames:
        amplitude = source_table['flux'] / (
            2. * np.pi * source_table['x_stddev'] * source_table['y_stddev'])
    elif 'amplitude' in source_table.colnames:
        amplitude = source_table['amplitude']
    else:
        raise ValueError('either "amplitude" or "flux" must be columns in '
                         'the input source_table')

    for i, source in enumerate(source_table):
        model = Gaussian2D(amplitude=amplitude[i],
                           x_mean=source['x_mean'],
                           y_mean=source['y_mean'],
                           x_stddev=source['x_stddev'],
                           y_stddev=source['y_stddev'],
                           theta=source['theta'])
        if oversample == 1:
            image += model(x, y)
        else:
            image += discretize_model(model, (0, image_shape[1]),
                                      (0, image_shape[0]),
                                      mode='oversample',
                                      factor=oversample)
    if unit is not None:
        image = u.Quantity(image, unit=unit)

    if wcs and not hdu:
        raise ValueError("wcs header only works with hdu output, use keyword "
                         "'hdu=True'")

    if hdu is True:
        from astropy.io import fits
        if wcs:
            from astropy.wcs import WCS
            if wcsheader is None:
                # Go with the simplest valid header
                header = WCS(
                    {
                        'CTYPE1': 'RA---TAN',
                        'CTYPE2': 'DEC--TAN',
                        'CRPIX1': int(image_shape[1] / 2),
                        'CRPIX2': int(image_shape[0] / 2)
                    }, ).to_header()
            else:
                header = WCS(wcsheader)
        else:
            header = None
        image = fits.ImageHDU(image, header=header)

    return image
Example #19
0
def make_model_sources(image_shape, model, source_table, oversample=1,
                       unit=None, hdu=False, wcs=False, wcsheader=None):
    """
    Make an image containing sources generated from a user-specified flux model.

    Parameters
    ----------
    image_shape : 2-tuple of int or `~numpy.ndarray`
        If a 2-tuple, shape of the output 2D image.  If an array, these sources
        will be *added* to that array.

    model : 2D astropy.modeling.models object
        The model to be used for rendering the sources.

    source_table : `~astropy.table.Table`
        Table of parameters for the sources.  The column names must
        match the model parameter names.  Column names that do not match
        model parameters will be ignored.  Any model parameter that is
        *not* in the table will be left at whatever value it has for
        ``model``.

    oversample : float, optional
        The sampling factor used to discretize the models on a
        pixel grid.

        If the value is 1.0 (the default), then the models will be
        discretized by taking the value at the center of the pixel bin.
        Note that this method will not preserve the total flux of very
        small sources.

        Otherwise, the models will be discretized by taking the average
        over an oversampled grid.  The pixels will be oversampled by the
        ``oversample`` factor.

    unit : `~astropy.units.UnitBase` instance, str, optional
        An object that represents the unit desired for the output image.
        Must be an `~astropy.units.UnitBase` object or a string
        parseable by the `~astropy.units` package.

    hdu : bool, optional
        If `True` returns ``image`` as an `~astropy.io.fits.ImageHDU`
        object.  To include WCS information in the header, use the
        ``wcs`` and ``wcsheader`` inputs.  Otherwise the header will be
        minimal.  Default is `False`.

    wcs : bool, optional
        If `True` and ``hdu=True``, then a simple WCS will be included
        in the returned `~astropy.io.fits.ImageHDU` header.  Default is
        `False`.

    wcsheader : dict or `None`, optional
        If ``hdu`` and ``wcs`` are `True`, this dictionary is passed to
        `~astropy.wcs.WCS` to generate the returned
        `~astropy.io.fits.ImageHDU` header.

    Returns
    -------
    image : `~numpy.ndarray` or `~astropy.units.Quantity` or `~astropy.io.fits.ImageHDU`
        Image or `~astropy.io.fits.ImageHDU` containing 2D Gaussian
        sources.
    """
    image = np.zeros(image_shape, dtype=np.float64)
    y, x = np.indices(image_shape)

    params_to_set = []
    for colnm in source_table.colnames:
        if colnm in model.param_names:
            params_to_set.append(colnm)

    # use this to store the *initial* values so we can set them back when done
    # with the loop.  Best not to copy a model, because some PSF models may have
    # substantial amounts of data in them
    init_params = {pnm: getattr(model, pnm) for pnm in params_to_set}

    try:
        for i, source in enumerate(source_table):
            for paramnm in params_to_set:
                setattr(model, paramnm, source[paramnm])
            if oversample == 1:
                image += model(x, y)
            else:
                image += discretize_model(model, (0, image_shape[1]),
                                          (0, image_shape[0]), mode='oversample',
                                          factor=oversample)
    finally:
        for pnm, val in init_params.items():
            setattr(model, paramnm, val)

    if unit is not None:
        image = u.Quantity(image, unit=unit)

    if wcs and not hdu:
        raise ValueError("wcs header only works with hdu output, use keyword "
                         "'hdu=True'")

    if hdu is True:
        from astropy.io import fits
        if wcs:
            from astropy.wcs import WCS
            if wcsheader is None:
                # Go with the simplest valid header
                header = WCS({'CTYPE1': 'RA---TAN',
                              'CTYPE2': 'DEC--TAN',
                              'CRPIX1': int(image_shape[1] / 2),
                              'CRPIX2': int(image_shape[0] / 2)}, ).to_header()
            else:
                header = WCS(wcsheader)
        else:
            header = None
        image = fits.ImageHDU(image, header=header)

    return image