Example #1
0
def circular_annulus_footprint(radius_inner, radius_outer, dtype=np.int):
    """
    Create a circular annulus footprint.

    A pixel is considered to be entirely in or out of the footprint
    depending on whether its center is in or out of the footprint.  The
    size of the output array is the minimal bounding box for the
    footprint.

    Parameters
    ----------
    radius_inner : int
        The inner radius of the circular annulus.

    radius_outer : int
        The outer radius of the circular annulus.

    dtype : data-type, optional
        The data type of the output `~numpy.ndarray`.

    Returns
    -------
    footprint : `~numpy.ndarray`
        A footprint where array elements are 1 within the footprint and
        0 otherwise.

    Examples
    --------
    >>> from astroimtools import circular_footprint
    >>> circular_annulus_footprint(1, 2)
    array([[0, 0, 1, 0, 0],
           [0, 1, 0, 1, 0],
           [1, 0, 0, 0, 1],
           [0, 1, 0, 1, 0],
           [0, 0, 1, 0, 0]])
    """

    if radius_inner > radius_outer:
        raise ValueError('radius_outer must be >= radius_inner')

    size = (radius_outer * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    circle_outer = Ellipse2D(1,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             theta=0)(x, y)
    circle_inner = Ellipse2D(1.,
                             radius_outer,
                             radius_outer,
                             radius_inner,
                             radius_inner,
                             theta=0)(x, y)
    return np.asarray(circle_outer - circle_inner, dtype=dtype)
Example #2
0
def ellipse_region(cube, center, a, ecc, theta, annulus = False):
    ''' Function that returns a mask that defines the elements that lie
        within an ellipsoidal region. The ellipse can also be annular.
        
        Inputs:
            Cube:    The data array
            center:  Tuple of the central coordinates (x_0, y_0) (spaxels)
            a:       The semi-major axis length (spaxels).
            ecc:     The eccentricity of the ellipse (0<ecc<1)
            theta:   The rotation angle of the ellipse from vertical (degrees)
                     rotating clockwise. 
            
        Optional Inputs:
            annulus: False if just simple ellipse, otherwise is the INNER
                     annular radius (spaxels)
        
    '''
    
    # Define angle, eccentricity term, and semi-minor axis
    an = Angle(theta, 'deg')
    e = np.sqrt(1 - (ecc**2))
    b = a * e
    
    # Create outer ellipse
    ell_region = Ellipse2D(amplitude=10, x_0 = center[0], y_0 = center[1],\
                a=a, b=b, theta=an.radian)
    x,y = np.mgrid[0:cube.shape[1], 0:cube.shape[2]]
    ell_good = ell_region(x,y)
    
    if annulus:
        # Define inner ellipse parameters and then create inner mask
        a2 = annulus
        b2 = a2 * e

        ell_region_inner = Ellipse2D(amplitude=10, x_0 = center[0],\
                y_0 = center[1], a=a2, b=b2, theta=an.radian)
        
        # Set region of outer mask and within inner ellipse to zero, leaving
        # an annular elliptical region
        ell_inner_good = ell_region_inner(x,y)
        ell_good[ell_inner_good > 0] = 0
        
    #fig, ax = mpl.subplots(figsize = (12,7))
    #mpl.imshow(np.nansum(cube, axis = 0), origin = 'lower', interpolation = None)
    #mpl.imshow(ell_good, alpha=0.2, origin = 'lower', interpolation = None)
    #mpl.show()
    
    # Return the mask
    return np.array(ell_good, dtype = bool)
Example #3
0
def intensity_props(data, blob, min_rad=4):
    '''
    Return the mean and std for the elliptical region in the given data.

    Parameters
    ----------
    data : LowerDimensionalObject or SpectralCube
        Data to estimate the background from.
    blob : numpy.array
        Contains the properties of the region.

    '''

    y, x, major, minor, pa = blob[:5]

    inner_ellipse = \
        Ellipse2D(True, x, y, max(min_rad, 0.75 * major),
                  max(min_rad, 0.75 * minor), pa)

    yy, xx = np.mgrid[:data.shape[-2], :data.shape[-1]]

    ellip_mask = inner_ellipse(xx, yy).astype(bool)

    vals = data[ellip_mask]

    mean, sig = robust_skewed_std(vals)

    return mean, sig
Example #4
0
def test_ellipse_extent():
    # Test this properly bounds the ellipse

    imshape = (100, 100)
    coords = y, x = np.indices(imshape)

    amplitude = 1
    x0 = 50
    y0 = 50
    a = 30
    b = 10
    theta = np.pi / 4

    model = Ellipse2D(amplitude, x0, y0, a, b, theta)

    dx, dy = ellipse_extent(a, b, theta)

    limits = ((y0 - dy, y0 + dy), (x0 - dx, x0 + dx))

    model.bounding_box = limits

    actual = model.render(coords=coords)

    expected = model(x, y)

    # Check that the full ellipse is captured
    np.testing.assert_allclose(expected, actual, atol=0, rtol=1)

    # Check the bounding_box isn't too large
    limits = np.array(limits).flatten()
    for i in [0, 1]:
        s = actual.sum(axis=i)
        diff = np.abs(limits[2 * i] - np.where(s > 0)[0][0])
        assert diff < 1
Example #5
0
def add_holes(shape, nholes=100, rad_max=30, rad_min=10, hole_level=None,
              return_info=False, max_corr=0.5, max_trials=500, max_eccent=2.):
    ylim, xlim = shape

    array = np.ones(shape) * 255

    yy, xx = np.mgrid[-ylim/2:ylim/2, -xlim/2:xlim/2]

    params = np.empty((0, 5))

    ct = 0
    trial = 0
    while True:

        xcenter = np.random.random_integers(-(xlim/2-rad_max),
                                            high=xlim/2-rad_max)
        ycenter = np.random.random_integers(-(ylim/2-rad_max),
                                            high=ylim/2-rad_max)
        major = np.random.uniform(rad_min, rad_max)
        minor = np.random.uniform(major / float(max_eccent), major)
        pa = np.random.uniform(0, np.pi)

        blob = np.array([ycenter, xcenter, major, minor, pa])

        if ct != 0:
            corrs = np.array([overlap_metric(par, blob, return_corr=True)
                              for par in params])
            if np.any(corrs > max_corr):
                trial += 1
                continue

        if hole_level is None:
            # Choose random hole bkg levels
            bkg = np.random.random_integers(0, 230)
        else:
            bkg = hole_level

        ellip = Ellipse2D(True, xcenter, ycenter, major, minor, pa)

        array[ellip(yy, xx).astype(bool)] = bkg

        params = np.append(params, blob[np.newaxis, :], axis=0)

        ct += 1
        trial += 1

        if ct == nholes:
            break

        if trial == max_trials:
            Warning("Only found %i valid regions in %i trials." %
                    (ct, max_trials))
            break

    if return_info:
        return array, params

    return array
def circular_annulus_footprint(radius_inner, radius_outer, dtype=np.int):
    if radius_inner > radius_outer:
        raise ValueError('radius_outer must be >= radius_inner')
    size = (radius_outer * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    circle_outer = Ellipse2D(1,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             theta=0)(x, y)
    circle_inner = Ellipse2D(1.,
                             radius_outer,
                             radius_outer,
                             radius_inner,
                             radius_inner,
                             theta=0)(x, y)
    return np.asarray(circle_outer - circle_inner, dtype=dtype)
Example #7
0
def fitEllipse(cc,axesLength,angle):
    x0, y0 = cc[0],cc[1]
    a, b = axesLength[0],axesLength[1]
    theta = Angle(angle, 'deg')
    e = Ellipse2D(amplitude=100., x_0=x0, y_0=y0, a=a, b=b, theta=theta.radian)
    y, x = np.mgrid[0:1000, 0:1000]
    center = PixCoord(x=x0, y=y0)
    reg = EllipsePixelRegion(center=center, width=2*a, height=2*b, angle=theta)
    patch = reg.as_artist(facecolor='none', edgecolor='red', lw=2)
    return reg
Example #8
0
def test_half_ellipse_in_fraction_rotate():

    blob = [70., 30., 20., 10., np.pi / 4.]

    ellip = Ellipse2D(True, blob[1], blob[0], blob[2], blob[3], blob[4])

    yy, xx = np.mgrid[:101, :1011]
    mask = ellip(yy, xx).astype(bool)

    np.testing.assert_allclose(fraction_in_mask(blob, mask), 1.0, rtol=0.01)
Example #9
0
def circular_annulus_footprint(radius_inner, radius_outer, dtype=np.int):
    """
    Create a circular annulus footprint.

    A pixel is considered to be entirely in or out of the footprint
    depending on whether its center is in or out of the footprint.  The
    size of the output array is the minimal bounding box for the
    footprint.

    Parameters
    ----------
    radius_inner : int
        The inner radius of the circular annulus.

    radius_outer : int
        The outer radius of the circular annulus.

    dtype : data-type, optional
        The data type of the output `~numpy.ndarray`.

    Returns
    -------
    footprint : `~numpy.ndarray`
        A footprint where array elements are 1 within the footprint and
        0 otherwise.
    """

    size = (radius_outer * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    circle_outer = Ellipse2D(1,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             radius_outer,
                             theta=0)(x, y)
    circle_inner = Ellipse2D(1.,
                             radius_outer,
                             radius_outer,
                             radius_inner,
                             radius_inner,
                             theta=0)(x, y)
    return np.asarray(circle_outer - circle_inner, dtype=dtype)
Example #10
0
def ellipse_in_array(params, shape):
    '''
    Test if the entire ellipse is within the given shape.
    '''
    yext, xext = Ellipse2D(True, params[1], params[0], params[2], params[3],
                           params[4]).bounding_box

    bottom_corner = np.array([floor_int(yext[0]), floor_int(xext[0])])
    top_corner = np.array([ceil_int(yext[1]), ceil_int(xext[1])])

    return in_array(bottom_corner, shape) and in_array(top_corner, shape)
Example #11
0
def elliptical_footprint(a, b, theta=0, dtype=np.int):
    """
    Create an elliptical footprint.

    A pixel is considered to be entirely in or out of the footprint
    depending on whether its center is in or out of the footprint.  The
    size of the output array is the minimal bounding box for the
    footprint.

    Parameters
    ----------
    a : int
        The semimajor axis.

    b : int
        The semiminor axis.

    theta : float, optional
        The rotation angle in radians of the semimajor axis.  The angle
        is measured counterclockwise from the positive x axis.

    dtype : data-type, optional
        The data type of the output `~numpy.ndarray`.

    Returns
    -------
    footprint : `~numpy.ndarray`
        A footprint where array elements are 1 within the footprint and
        0 otherwise.

    Examples
    --------
    >>> import numpy as np
    >>> from astroimtools import elliptical_footprint
    >>> elliptical_footprint(3, 1, theta=np.pi/4.)
    array([[1, 1, 0, 0, 0],
           [1, 1, 1, 0, 0],
           [0, 1, 1, 1, 0],
           [0, 0, 1, 1, 1],
           [0, 0, 0, 1, 1]])
    """

    if b > a:
        raise ValueError('a must be >= b')

    size = (a * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    ellipse = Ellipse2D(1, a, a, a, b, theta=theta)(x, y)

    # crop to minimal bounding box
    yi, xi = ellipse.nonzero()
    idx = (slice(min(yi), max(yi) + 1), slice(min(xi), max(xi) + 1))
    return np.asarray(ellipse[idx], dtype=dtype)
Example #12
0
File: log.py Project: e-koch/BaSiCs
def shape_from_blob_moments(blob, response, expand_factor=np.sqrt(2)):
    '''
    Use blob properties to define a region, then correct its shape by
    using the response surface
    '''
    yy, xx = np.mgrid[:response.shape[0], :response.shape[1]]

    mask = Ellipse2D.evaluate(yy, xx, True, blob[0],
                              blob[1], expand_factor * blob[2],
                              expand_factor * blob[3], blob[4]).astype(np.int)

    resp = response.copy()
    # We don't care about the negative values here, so set to 0
    resp[resp < 0.0] = 0.0

    props = regionprops(mask, intensity_image=resp)[0]

    # Now use the moments to refine the shape
    wprops = weighted_props(props)
    y, x, major, minor, pa = wprops

    new_mask = Ellipse2D.evaluate(yy, xx, True, y,
                                  x, major,
                                  minor, pa).astype(bool)

    # Find the maximum value in the response
    max_resp = np.max(response[new_mask])

    # print(blob)
    # print((y, x, major, minor, pa, max_resp))
    # import matplotlib.pyplot as p
    # p.imshow(response, origin='lower', cmap='afmhot')
    # p.contour(mask, colors='r')
    # p.contour(new_mask, colors='g')
    # p.draw()
    # import time; time.sleep(0.1)
    # raw_input("?")
    # p.clf()

    return np.array([y, x, major, minor, pa, max_resp])
Example #13
0
def shape_from_blob_moments(blob, response, expand_factor=np.sqrt(2)):
    '''
    Use blob properties to define a region, then correct its shape by
    using the response surface
    '''
    yy, xx = np.mgrid[:response.shape[0], :response.shape[1]]

    mask = Ellipse2D.evaluate(yy, xx, True, blob[0], blob[1],
                              expand_factor * blob[2], expand_factor * blob[3],
                              blob[4]).astype(np.int)

    resp = response.copy()
    # We don't care about the negative values here, so set to 0
    resp[resp < 0.0] = 0.0

    props = regionprops(mask, intensity_image=resp)[0]

    # Now use the moments to refine the shape
    wprops = weighted_props(props)
    y, x, major, minor, pa = wprops

    new_mask = Ellipse2D.evaluate(yy, xx, True, y, x, major, minor,
                                  pa).astype(bool)

    # Find the maximum value in the response
    max_resp = np.max(response[new_mask])

    # print(blob)
    # print((y, x, major, minor, pa, max_resp))
    # import matplotlib.pyplot as p
    # p.imshow(response, origin='lower', cmap='afmhot')
    # p.contour(mask, colors='r')
    # p.contour(new_mask, colors='g')
    # p.draw()
    # import time; time.sleep(0.1)
    # raw_input("?")
    # p.clf()

    return np.array([y, x, major, minor, pa, max_resp])
Example #14
0
    def __init__(self, stddev_maj, stddev_min, position_angle, support_scaling=1,
                 **kwargs):

        self._model = Ellipse2D(1. / (np.pi * stddev_maj * stddev_min), 0, 0,
                                stddev_maj, stddev_min, position_angle)

        try:
            from astropy.modeling.utils import ellipse_extent
        except ImportError:
            raise NotImplementedError("EllipticalTophat2DKernel requires"
                                      " astropy 1.1b1 or greater.")

        max_extent = \
            np.max(ellipse_extent(stddev_maj, stddev_min, position_angle))
        self._default_size = \
            _round_up_to_odd_integer(support_scaling * 2 * max_extent)
        super(EllipticalTophat2DKernel, self).__init__(**kwargs)
        self._truncation = 0
Example #15
0
def fp_wat_cal(wat_map,
               angle,
               fp_row,
               fp_col,
               a=27.3,
               b=5,
               window_row=101,
               window_col=101):
    '''
    obtain the statistic corresponding to the footprint region of the image
    wat_map: 2-d numpy.array, water map
    angle: degree(unit), direction of the ellipse
    fp_row, fp_col: location of the footprint.
    a,b: the length corresponding to the semimajor axis and semiminor axis respectively.
    window_row,window_col: the sub-region size of water map with the centered footprint.
    '''
    row_map, col_map = wat_map.shape
    center_row = round((window_row - 1) / 2)
    center_col = round((window_col - 1) / 2)
    if col_map - fp_col < center_col + 1:
        wat_map = np.pad(wat_map, ((0, 0), (0, center_col + 1)),
                         constant_values=0)
    if row_map - fp_row < center_row + 1:
        wat_map = np.pad(wat_map, ((0, center_row + 1), (0, 0)),
                         constant_values=0)
    wat_map_window = wat_map[fp_row - center_row:fp_row + center_row + 1,
                             fp_col - center_col:fp_col + center_col + 1]
    theta = Angle(angle, 'deg')
    y, x = np.mgrid[0:window_row, 0:window_col]
    e = Ellipse2D(amplitude=1.,
                  x_0=center_col,
                  y_0=center_row,
                  a=a,
                  b=b,
                  theta=theta.radian)
    mask = e(x, y)
    ## from botton-letf coordinate to up-left(image col-row) coordinate
    mask = np.flip(mask, axis=0)
    values = wat_map_window[mask == 1]
    wat_percent = np.sum(values) / values.shape
    return wat_percent
Example #16
0
def elliptical_footprint(a, b, theta=0, dtype=np.int):
    """
    Create an elliptical footprint.

    A pixel is considered to be entirely in or out of the footprint
    depending on whether its center is in or out of the footprint.  The
    size of the output array is the minimal bounding box for the
    footprint.

    Parameters
    ----------
    a : int
        The semimajor axis.

    b : int
        The semiminor axis.

    theta : float, optional
        The angle in radians of the semimajor axis.  The angle is
        measured counterclockwise from the positive x axis.

    dtype : data-type, optional
        The data type of the output `~numpy.ndarray`.

    Returns
    -------
    footprint : `~numpy.ndarray`
        A footprint where array elements are 1 within the footprint and
        0 otherwise.
    """

    size = (a * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    ellipse = Ellipse2D(1, a, a, a, b, theta=theta)(x, y)

    # crop to minimal bounding box
    yi, xi = ellipse.nonzero()
    idx = (slice(min(yi), max(yi) + 1), slice(min(xi), max(xi) + 1))
    return np.asarray(ellipse[idx], dtype=dtype)
Example #17
0
def fp_mask(img, a, b, center_row, center_col, angle):
    '''
    obtain the ellipse mask of the image
    img: 2-d numpy.array;
    a,b: the length corresponding to the semimajor axis and semiminor axis respectively.
    center_row,center_col: the center location
    '''
    if len(img.shape) == 3:
        img_row, img_col, _ = img.shape
    else:
        img_row, img_col = img.shape
    theta = Angle(angle, 'deg')
    y, x = np.mgrid[0:img_row, 0:img_col]
    e = Ellipse2D(amplitude=1.,
                  x_0=center_col,
                  y_0=center_row,
                  a=a,
                  b=b,
                  theta=theta.radian)
    mask = e(x, y)
    ## from botton-letf coordinate to up-left(image col-row) coordinate
    mask = np.flip(mask, axis=0)
    return mask
Example #18
0
def elliptical_annulus_footprint(a_inner,
                                 a_outer,
                                 b_inner,
                                 theta=0,
                                 dtype=np.int):
    """
    Create an elliptical annulus footprint.

    A pixel is considered to be entirely in or out of the footprint
    depending on whether its center is in or out of the footprint.  The
    size of the output array is the minimal bounding box for the
    footprint.

    Parameters
    ----------
    a_inner : int
        The inner semimajor axis.

    a_outer : int
        The outer semimajor axis.

    b_inner : int
        The inner semiminor axis.  The outer semiminor axis is
        calculated using the same axis ratio as the semimajor axis:

        .. math::
            b_{outer} = b_{inner} \\left( \\frac{a_{outer}}{a_{inner}}
            \\right)

    theta : float, optional
        The rotation angle in radians of the semimajor axis.  The angle
        is measured counterclockwise from the positive x axis.

    dtype : data-type, optional
        The data type of the output `~numpy.ndarray`.

    Returns
    -------
    footprint : `~numpy.ndarray`
        A footprint where array elements are 1 within the footprint and
        0 otherwise.

    Examples
    --------
    >>> import numpy as np
    >>> from astroimtools import elliptical_annulus_footprint
    >>> elliptical_annulus_footprint(2, 4, 1, theta=np.pi/4.)
    array([[0, 1, 1, 0, 0, 0, 0],
           [1, 1, 1, 1, 0, 0, 0],
           [1, 1, 0, 0, 1, 0, 0],
           [0, 1, 0, 0, 0, 1, 0],
           [0, 0, 1, 0, 0, 1, 1],
           [0, 0, 0, 1, 1, 1, 1],
           [0, 0, 0, 0, 1, 1, 0]])
    """

    if a_inner > a_outer:
        raise ValueError('a_outer must be >= a_inner')

    if b_inner > a_inner:
        raise ValueError('a_inner must be >= b_inner')

    size = (a_outer * 2) + 1
    y, x = np.mgrid[0:size, 0:size]
    b_outer = b_inner * (a_outer / a_inner)
    ellipse_outer = Ellipse2D(1,
                              a_outer,
                              a_outer,
                              a_outer,
                              b_outer,
                              theta=theta)(x, y)
    ellipse_inner = Ellipse2D(1,
                              a_outer,
                              a_outer,
                              a_inner,
                              b_inner,
                              theta=theta)(x, y)
    annulus = ellipse_outer - ellipse_inner

    # crop to minimal bounding box
    yi, xi = annulus.nonzero()
    idx = (slice(min(yi), max(yi) + 1), slice(min(xi), max(xi) + 1))
    return np.asarray(annulus[idx], dtype=dtype)
Example #19
0
import numpy as np
from astropy.io import fits
from astropy.modeling.models import Ellipse2D

(x_0, y_0) = (1000., 2500.)  # Center of ellipse, pixel coords
(a, b) = (500., 300.)  # Major and minor axes of ellipse, pixel coords
theta = np.pi/4  # Orientation of ellipse, radians
ellipse = Ellipse2D(amplitude=1., x_0=x_0, y_0=y_0,
                    a=a, b=b, theta=theta) 

img_filename = 'elp1m008-fl05-20170101-0119-e91.fits'  # Example image I had access to
with fits.open(img_filename) as hdul:

    data = hdul[0].data
    y, x = np.mgrid[0:data.shape[0], 0:data.shape[1]]
    ellipse_mask = ellipse(x, y).astype(bool)
    cropped_data = np.where(ellipse_mask, data, np.nan)
    hdul[0].data = cropped_data
    hdul.writeto('cropped_image.fits')

Example #20
0
def find_bubble_edges(array,
                      blob,
                      max_extent=1.0,
                      edge_mask=None,
                      nsig_thresh=1,
                      value_thresh=None,
                      radius=None,
                      return_mask=False,
                      min_pixels=16,
                      filter_size=4,
                      verbose=False,
                      min_radius_frac=0.0,
                      try_local_bkg=True,
                      **kwargs):
    '''
        Expand/contract to match the contours in the data.

        Parameters
        ----------
        array : 2D numpy.ndarray or spectral_cube.LowerDimensionalObject
            Data used to define the region boundaries.
        max_extent : float, optional
            Multiplied by the major radius to set how far should be searched
            when searching for the boundary.
        nsig_thresh : float, optional
            Number of times sigma above the mean to set the boundary intensity
            requirement. This is used whenever the local background is higher
            than the given `value_thresh`.
        value_thresh : float, optional
            When given, sets the minimum intensity for defining a bubble edge.
            The natural choice is a few times the noise level in the cube.
        radius : float, optional
            Give an optional radius to use instead of the major radius defined
            for the bubble.
        kwargs : passed to profile.profile_line.

        Returns
        -------
        extent_coords : np.ndarray
            Array with the positions of edges.
        '''

    if try_local_bkg:
        mean, std = intensity_props(array, blob)
        background_thresh = mean + nsig_thresh * std

        # Define a suitable background based on the intensity within the
        # elliptical region
        if value_thresh is None:
            value_thresh = background_thresh
        else:
            # If value_thresh is higher use it. Otherwise use the bkg.
            if value_thresh < background_thresh:
                value_thresh = background_thresh

    # Set the number of theta to be ~ the perimeter.

    y, x, major, minor, pa = blob[:5]
    y = int(np.round(y, decimals=0))
    x = int(np.round(x, decimals=0))

    # If the center is on the edge of the array, subtract one to
    # index correctly
    if y == array.shape[0]:
        y -= 1
    if x == array.shape[1]:
        x -= 1

    # Use the ellipse model to define a bounding box for the mask.
    bbox = Ellipse2D(True, 0.0, 0.0, major * max_extent, minor * max_extent,
                     pa).bounding_box

    y_range = ceil_int(bbox[0][1] - bbox[0][0] + 1 + filter_size)
    x_range = ceil_int(bbox[1][1] - bbox[1][0] + 1 + filter_size)

    shell_thetas = []

    yy, xx = np.mgrid[-int(y_range / 2):int(y_range / 2) + 1,
                      -int(x_range / 2):int(x_range / 2) + 1]

    if edge_mask is not None:
        arr = edge_mask[max(0, y - int(y_range / 2)):y + int(y_range / 2) + 1,
                        max(0, x - int(x_range / 2)):x + int(x_range / 2) + 1]
    else:
        arr = array[max(0, y - int(y_range / 2)):y + int(y_range / 2) + 1,
                    max(0, x - int(x_range / 2)):x + int(x_range / 2) + 1]

    # Adjust meshes if they exceed the array shape
    x_min = -min(0, x - int(x_range / 2))
    x_max = xx.shape[1] - max(0, x + int(x_range / 2) - array.shape[1] + 1)
    y_min = -min(0, y - int(y_range / 2))
    y_max = yy.shape[0] - max(0, y + int(y_range / 2) - array.shape[0] + 1)

    offset = (max(0, int(y - (y_range / 2))), max(0, int(x - (x_range / 2))))

    yy = yy[y_min:y_max, x_min:x_max]
    xx = xx[y_min:y_max, x_min:x_max]

    dist_arr = np.sqrt(yy**2 + xx**2)

    if edge_mask is not None:
        smooth_mask = arr
    else:
        smooth_mask = \
            smooth_edges(arr <= value_thresh, filter_size, min_pixels)

    region_mask = \
        Ellipse2D(True, 0.0, 0.0, major * max_extent, minor * max_extent,
                  pa)(xx, yy).astype(bool)
    region_mask = nd.binary_dilation(region_mask, eight_conn, iterations=2)

    # The bubble center must fall within a valid region
    mid_pt = np.where(dist_arr == 0.0)
    if len(mid_pt[0]) == 0:
        middle_fail = True
    else:
        local_center = zip(*np.where(dist_arr == 0.0))[0]
        middle_fail = False
    # _make_bubble_mask(smooth_mask, local_center)

    # If the center is not contained within a bubble region, return
    # empties.
    bad_case = not smooth_mask.any() or smooth_mask.all() or \
        (smooth_mask * region_mask).all() or middle_fail
    if bad_case:
        if return_mask:
            return np.array([]), 0.0, 0.0, value_thresh, smooth_mask

        return np.array([]), 0.0, 0.0, value_thresh

    orig_perim = find_contours(region_mask, 0, fully_connected='high')[0]
    # new_perim = find_contours(smooth_mask, 0, fully_connected='high')
    coords = []
    extent_mask = np.zeros_like(region_mask)
    # for perim in new_perim:
    #     perim = perim.astype(np.int)
    #     good_pts = \
    #         np.array([pos for pos, pt in enumerate(perim)
    #                   if region_mask[pt[0], pt[1]]])
    #     if not good_pts.any():
    #         continue

    #     # Now split into sections
    #     from utils import consec_split
    #     split_pts = consec_split(good_pts)

    #     # Remove the duplicated end point if it was initially connected
    #     if len(split_pts) > 1:
    #         # Join these if the end pts initially matched
    #         if split_pts[0][0] == split_pts[-1][-1]:
    #             split_pts[0] = np.append(split_pts[0],
    #                                      split_pts[-1][::-1])
    #             split_pts.pop(-1)

    #     for split in split_pts:
    #         coords.append(perim[split])

    #     extent_mask[perim[good_pts][:, 0], perim[good_pts][:, 1]] = True

    # Based on the curvature of the shell, only fit points whose
    # orientation matches the assumed centre.
    # incoord, outcoord = shell_orientation(coords, local_center,
    #                                       verbose=False)

    # Now only keep the points that are not blocked from the centre pixel
    for pt in orig_perim:

        theta = np.arctan2(pt[0] - local_center[0], pt[1] - local_center[1])

        num_pts = int(
            np.round(np.hypot(pt[0] - local_center[0],
                              pt[1] - local_center[1]),
                     decimals=0))

        ys = np.round(np.linspace(local_center[0], pt[0], num_pts),
                      decimals=0).astype(np.int)

        xs = np.round(np.linspace(local_center[1], pt[1], num_pts),
                      decimals=0).astype(np.int)

        not_on_edge = np.logical_and(ys < smooth_mask.shape[0],
                                     xs < smooth_mask.shape[1])
        ys = ys[not_on_edge]
        xs = xs[not_on_edge]

        dist = np.sqrt((ys - local_center[0])**2 + (xs - local_center[1])**2)

        prof = smooth_mask[ys, xs]

        prof = prof[dist >= min_radius_frac * minor]
        ys = ys[dist >= min_radius_frac * minor]
        xs = xs[dist >= min_radius_frac * minor]

        # Look for the first 0 and ignore all others past it
        zeros = np.where(prof == 0)[0]

        # If none, move on
        if not zeros.any():
            continue

        edge = zeros[0]

        extent_mask[ys[edge], xs[edge]] = True
        coords.append((ys[edge], xs[edge]))
        shell_thetas.append(theta)

    # Calculate the fraction of the region associated with a shell
    shell_frac = len(shell_thetas) / float(len(orig_perim))

    shell_thetas = np.array(shell_thetas)
    coords = np.array(coords)

    # Use the theta values to find the standard deviation i.e. how
    # dispersed the shell locations are. Assumes a circle, but we only
    # consider moderately elongated ellipses, so the statistics approx.
    # hold.
    theta_var = np.sqrt(circvar(shell_thetas * u.rad)).value

    extent_coords = \
        np.vstack([pt + off for pt, off in
                   zip(np.where(extent_mask), offset)]).T

    if verbose:
        print("Shell fraction : " + str(shell_frac))
        print("Angular Std. : " + str(theta_var))
        import matplotlib.pyplot as p
        true_region_mask = \
            Ellipse2D(True, 0.0, 0.0, major, minor,
                      pa)(xx, yy).astype(bool)

        ax = p.subplot(121)
        ax.imshow(arr, origin='lower', interpolation='nearest')
        ax.contour(smooth_mask, colors='b')
        ax.contour(region_mask, colors='r')
        ax.contour(true_region_mask, colors='g')
        if len(coords) > 0:
            p.plot(coords[:, 1], coords[:, 0], 'bD')
        p.plot(local_center[1], local_center[0], 'gD')
        ax2 = p.subplot(122)
        ax2.imshow(extent_mask, origin='lower', interpolation='nearest')
        p.draw()
        raw_input("?")
        p.clf()

    if return_mask:
        return extent_coords, shell_frac, theta_var, value_thresh, \
            extent_mask

    return extent_coords, shell_frac, theta_var, value_thresh
Example #21
0
File: log.py Project: e-koch/BaSiCs
def _ellipse_overlap(blob1, blob2, grid_space=0.5, return_corr=False):
    '''
    Ellipse intersection are difficult. But counting common pixels is not!
    This routine creates arrays up-sampled from the original pixel scale to
    better estimate the overlap fraction.
    '''

    ellip1_area = np.pi * blob1[2] * blob1[3]
    ellip2_area = np.pi * blob2[2] * blob2[3]

    if ellip1_area >= ellip2_area:
        large_blob = blob1
        small_blob = blob2
        large_ellip_area = ellip1_area
        small_ellip_area = ellip2_area
    else:
        large_blob = blob2
        small_blob = blob1
        large_ellip_area = ellip2_area
        small_ellip_area = ellip1_area

    bound1 = Ellipse2D(True, 0.0, 0.0, large_blob[2], large_blob[3],
                       large_blob[4]).bounding_box

    bound2 = Ellipse2D(True, small_blob[1]-large_blob[1],
                       small_blob[0]-large_blob[0],
                       small_blob[2], small_blob[3],
                       small_blob[4]).bounding_box

    # If there is no overlap in the bounding boxes, there is no overlap
    if bound1[0][1] <= bound2[0][0] or bound1[1][1] <= bound2[1][0]:
        return 0.0

    # Now check if the smaller one is completely inside the larger
    low_in_large = bound1[0][0] <= bound2[0][0] and \
        bound1[0][1] >= bound2[0][1]
    high_in_large = bound1[1][0] <= bound2[1][0] and \
        bound1[1][1] >= bound2[1][1]

    if low_in_large and high_in_large:
        if return_corr:
            # Aover / sqrt(A1 * A2) = A1 / sqrt(A1 * A2) = sqrt(A1/A2)
            return np.sqrt(small_ellip_area / large_ellip_area)
        return 1.0

    # Only evaluate the overlap between the two.

    ybounds = (max(bound1[0][0], bound2[0][0]),
               min(bound1[0][1], bound2[0][1]))

    xbounds = (max(bound1[1][0], bound2[1][0]),
               min(bound1[1][1], bound2[1][1]))

    yy, xx = \
        np.meshgrid(np.arange(ybounds[0] - grid_space, ybounds[1] + grid_space,
                              grid_space),
                    np.arange(xbounds[0] - grid_space, xbounds[1] + grid_space,
                              grid_space))

    ellip1 = Ellipse2D.evaluate(xx, yy, True, 0.0,
                                0.0, large_blob[2],
                                large_blob[3], large_blob[4])

    ellip2 = Ellipse2D.evaluate(xx, yy, True,
                                small_blob[1]-large_blob[1],
                                small_blob[0]-large_blob[0],
                                small_blob[2], small_blob[3],
                                small_blob[4])

    overlap_area = np.sum(np.logical_and(ellip1, ellip2)) * grid_space ** 2

    if return_corr:
        return overlap_area / np.sqrt(small_ellip_area * large_ellip_area)

    return overlap_area / small_ellip_area
Example #22
0
def _ellipse_overlap(blob1, blob2, grid_space=0.5, return_corr=False):
    '''
    Ellipse intersection are difficult. But counting common pixels is not!
    This routine creates arrays up-sampled from the original pixel scale to
    better estimate the overlap fraction.
    '''

    ellip1_area = np.pi * blob1[2] * blob1[3]
    ellip2_area = np.pi * blob2[2] * blob2[3]

    if ellip1_area >= ellip2_area:
        large_blob = blob1
        small_blob = blob2
        large_ellip_area = ellip1_area
        small_ellip_area = ellip2_area
    else:
        large_blob = blob2
        small_blob = blob1
        large_ellip_area = ellip2_area
        small_ellip_area = ellip1_area

    bound1 = Ellipse2D(True, 0.0, 0.0, large_blob[2], large_blob[3],
                       large_blob[4]).bounding_box

    bound2 = Ellipse2D(True, small_blob[1] - large_blob[1],
                       small_blob[0] - large_blob[0], small_blob[2],
                       small_blob[3], small_blob[4]).bounding_box

    # If there is no overlap in the bounding boxes, there is no overlap
    if bound1[0][1] <= bound2[0][0] or bound1[1][1] <= bound2[1][0]:
        return 0.0

    # Now check if the smaller one is completely inside the larger
    low_in_large = bound1[0][0] <= bound2[0][0] and \
        bound1[0][1] >= bound2[0][1]
    high_in_large = bound1[1][0] <= bound2[1][0] and \
        bound1[1][1] >= bound2[1][1]

    if low_in_large and high_in_large:
        if return_corr:
            # Aover / sqrt(A1 * A2) = A1 / sqrt(A1 * A2) = sqrt(A1/A2)
            return np.sqrt(small_ellip_area / large_ellip_area)
        return 1.0

    # Only evaluate the overlap between the two.

    ybounds = (max(bound1[0][0], bound2[0][0]), min(bound1[0][1],
                                                    bound2[0][1]))

    xbounds = (max(bound1[1][0], bound2[1][0]), min(bound1[1][1],
                                                    bound2[1][1]))

    yy, xx = \
        np.meshgrid(np.arange(ybounds[0] - grid_space, ybounds[1] + grid_space,
                              grid_space),
                    np.arange(xbounds[0] - grid_space, xbounds[1] + grid_space,
                              grid_space))

    ellip1 = Ellipse2D.evaluate(xx, yy, True, 0.0, 0.0, large_blob[2],
                                large_blob[3], large_blob[4])

    ellip2 = Ellipse2D.evaluate(xx, yy, True, small_blob[1] - large_blob[1],
                                small_blob[0] - large_blob[0], small_blob[2],
                                small_blob[3], small_blob[4])

    overlap_area = np.sum(np.logical_and(ellip1, ellip2)) * grid_space**2

    if return_corr:
        return overlap_area / np.sqrt(small_ellip_area * large_ellip_area)

    return overlap_area / small_ellip_area
Example #23
0
def galaxy_mask(header,leda):

    ra, dec, theta, diam, ba = leda

    sz = (header["NAXIS1"], header["NAXIS2"])
    outmsk = np.zeros((sz[0],sz[1]),dtype=bool)
    w = WCS(header)

    ##CCD corners (these are actually in the header for DECam)
    xc = [0,0,sz[0]-1,sz[0]-1]
    yc = [0,sz[1]-1,0,sz[1]-1]
    xcen = (sz[0]-1)/2
    ycen = (sz[1]-1)/2

    c = w.pixel_to_world(xcen,ycen)
    sep = c.separation(w.pixel_to_world(xc, yc))

    racen = c.ra.degree
    deccen = c.dec.degree

    dac = np.max(sep.degree)
    coordleda = SkyCoord(ra,dec,frame='icrs',unit='deg')
    dangle = c.separation(coordleda).degree

    keep = dangle < dac + diam
    #filter out galaxies with too large angle displace to consider
    if sum(keep) == 0:
        return outmsk

    relevant = w.world_to_pixel(coordleda[keep])

    with warnings.catch_warnings():
        warnings.simplefilter('ignore', category=(AstropyWarning,RuntimeWarning))
        wperm = Wcsprm(header=header.tostring().encode('utf-8'))
        cd = wperm.cd #* wperm.cdelt[np.mod(np.linspace(0,3,4,dtype=int),2).reshape(2,2)]
    pscl = np.mean(np.sqrt(np.sum(cd**2,axis=1)))*3600 #convert back to arcsec from deg for interp
    msz = np.ceil(3600*diam[keep]/pscl).astype(int) #this is the mask size in the pixel scale
    msz += np.mod(msz,2) == 0 #adjust even size to be odd so that the center means something
    mh = (msz-1)//2 #I think this -1 is necessary becase we are all odd by construction

    ix = np.round(relevant[0]).astype(int)
    iy = np.round(relevant[1]).astype(int)

    #largest circle approx must fall in ccd
    insideim = ((ix+mh) >= 0) & ((ix-mh) < sz[0]) & ((iy+mh) >= 0) & (iy-mh < sz[1])
    nim = sum(insideim)

    #filter out galaxy with no overlap
    if nim == 0:
        return outmsk

    rainl = coordleda[keep][insideim].ra.degree
    decinl = coordleda[keep][insideim].dec.degree
    thetal = np.deg2rad(theta[keep][insideim])

    print("Masked Galaxies: %d" % nim)
    for i in range(0,nim):
        un, ue = tan_unit_vectors(rainl[i],decinl[i],racen,deccen)

        ueofn = un*np.cos(thetal[i])+ue*np.sin(thetal[i])
        angle = np.rad2deg(np.arctan2(ueofn[1],ueofn[0]))

        ixn = ix[insideim][i]
        iyn = iy[insideim][i]
        mhn = mh[insideim][i]
        mszn = msz[insideim][i]

        e = Ellipse2D(x_0=mhn, y_0=mhn, a=mhn, b=ba[keep][insideim][i]*mhn,
                      theta=np.deg2rad(angle))
        y, x = np.mgrid[0:mszn, 0:mszn]
        smsk = (e(x, y) == 1)

        outmsk[np.clip(ixn-mhn,a_min=0,a_max=None):np.clip(ixn+mhn+1,a_min=None,a_max=sz[0]),
           np.clip(iyn-mhn,a_min=0,a_max=None):np.clip(iyn+mhn+1,a_min=None,a_max=sz[1])] |= smsk[
        np.clip(mhn-ixn,a_min=0,a_max=None):np.clip(sz[0]-ixn+mhn,a_min=None,a_max=mszn),
             np.clip(mhn-iyn,a_min=0,a_max=None):np.clip(sz[1]-iyn+mhn,a_min=None,a_max=mszn)]
    return outmsk.T #revist need for transpose