示例#1
0
def first_cc_val_neg(param, *args):

    center_x, center_y = param
    data = args[0]
    radius_size = args[1]
    ones = np.array([[1] * 600] * 600)

    center_ap = CircularAperture([center_x, center_y], radius_size)
    center_area = center_ap.area
    center_mask = center_ap.to_mask(method='exact')
    center_data = center_mask.multiply(data)
    center_weights = center_mask.multiply(ones)
    center_std = twoD_weighted_std(center_data, center_weights)
    center_val = (np.sum(center_data)) / center_area + 5 * center_std

    first_ap = CircularAnnulus([center_x, center_y],
                               r_in=radius_size,
                               r_out=2 * radius_size)
    first_area = first_ap.area
    first_mask = first_ap.to_mask(method='exact')
    first_data = first_mask.multiply(data)
    first_weights = first_mask.multiply(ones)
    first_std = twoD_weighted_std(first_data, first_weights)
    first_val = (np.sum(first_data)) / first_area + 5 * first_std

    result = (-2.5 * math.log(first_val / center_val, 10))

    return -1 * (result)
示例#2
0
 def compute_circular_aperture(self, centre, radius, flux_return=False,
                               plot=False):
     from photutils.aperture import CircularAperture#, aperture_photometry
     aperture = CircularAperture(centre, r=radius)
     aperture_mask = aperture.to_mask()
     aperture_mask = np.array(
         aperture_mask.to_image(self.wcs.celestial.array_shape), dtype=bool)
     
     if flux_return:
         integrated_flux = np.nansum(self.flux[:, aperture_mask], axis=1)
         integrated_no_cov = np.nansum(self.flux_error[:, aperture_mask]**2, axis=1)
         ## Accounting for covariance errors
         if aperture_mask[aperture_mask].size<100:
             integrated_flux_cov = integrated_no_cov*(
                 1+1.62*np.log10(aperture_mask[aperture_mask].size))
             integrated_flux_err = np.sqrt(integrated_flux_cov)
         else:
             integrated_flux_cov = integrated_no_cov*4.2
             integrated_flux_err = np.sqrt(integrated_flux_cov)
     else:
         integrated_flux = None
         integrated_flux_err = None
     if plot:
         fig = plt.figure()
         ax = fig.add_subplot(111)
         ax.imshow(self.flux[self.wl.size//2, :, :], cmap='gist_earth_r')            
         aperture.plot(lw=1, color='r')
         ax.annotate(r'$R_e={:4.3}~(Kpc)$'.format(self.eff_radius_physical),
                     xy=(.9,.95), xycoords='axes fraction', va='top', ha='right')            
         ax.annotate(r'$R_e={:4.3}~(arcsec)$'.format(self.eff_radius),
                     xy=(.9,.9), xycoords='axes fraction', va='top', ha='right')
         aperture.plot(lw=1, color='r')
         ax.annotate(r'$R_e={:4.3}~(pix)$'.format(self.eff_radius_pix),
                     xy=(.9,.85), xycoords='axes fraction', va='top', ha='right')
         ax.annotate(r'$R_a={:4.3}~(pix)$'.format(radius),
                     xy=(.9,.80), xycoords='axes fraction', va='top', ha='right',
                     color='r')
         aperture.plot(lw=1, color='r')
         # plt.savefig('bpt_apertures/'+name_i+'.png')
         plt.show()
         plt.close()
         return aperture, aperture_mask, integrated_flux, integrated_flux_err, fig 
     else:           
         return aperture, aperture_mask, integrated_flux, integrated_flux_err    
示例#3
0
def ConCur(star_data,
           radius_size=1,
           center=None,
           background_method='astropy',
           find_hots=False,
           find_center=False):

    data = star_data.copy()

    background_mean, background_std = background_calc(data, background_method)

    x, y = np.indices((data.shape))

    if not center:
        center = np.array([(x.max() - x.min()) / 2.0,
                           (y.max() - y.min()) / 2.0])

    if find_hots == True:
        hots = hot_pixels(data, center, background_mean, background_std)

    if find_center == True:
        center_vals = find_best_center(data, radius_size, center)
        center = np.array([center_vals[0], center_vals[1]])

    radii = np.sqrt((x - center[0])**2 + (y - center[1])**2)
    radii = radii.astype(np.int)

    ones = np.array([[1] * len(data)] * len(data[0]))

    number_of_a = radii.max() / radius_size

    center_ap = CircularAperture([center[0], center[1]], radius_size)

    all_apers, all_apers_areas, all_masks = [center_ap], [center_ap.area], [
        center_ap.to_mask(method='exact')
    ]

    all_data, all_weights = [all_masks[0].multiply(data)
                             ], [all_masks[0].multiply(ones)]

    all_stds = [twoD_weighted_std(all_data[0], all_weights[0])]

    for j in range(int(number_of_a)):
        aper = CircularAnnulus([center[0], center[1]],
                               r_in=(j * radius_size + radius_size),
                               r_out=(j * radius_size + 2 * radius_size))
        all_apers.append(aper)
        all_apers_areas.append(aper.area)
        mask = aper.to_mask(method='exact')
        all_masks.append(mask)
        mask_data = mask.multiply(data)
        mask_weight = mask.multiply(ones)
        all_data.append(mask_data)
        all_weights.append(mask_weight)
        all_stds.append(twoD_weighted_std(mask_data, mask_weight))
    phot_table = aperture_photometry(data, all_apers)

    center_val = np.sum(all_data[0]) / all_apers_areas[0] + 5 * all_stds[0]

    delta_mags = []
    for i in range(len(phot_table[0]) - 3):
        try:
            delta_mags.append(-2.5 * math.log((np.sum(all_data[i])/all_apers_areas[i] + \
                                               5*all_stds[i])/center_val,10))
        except ValueError:
            print('annulus',i, 'relative flux equal to', (np.sum(all_data[i])/all_apers_areas[i] + \
                                               5*all_stds[i])/center_val, '...it is not included')
            delta_mags.append(np.NaN)

    arc_lengths = []
    for i in range(len(delta_mags)):
        arc_lengths.append(
            (i * 0.033 + 0.033) *
            radius_size)  #make sure center radius size is correct
    arc_lengths = np.array(arc_lengths)
    lim_arc_lengths = arc_lengths[arc_lengths < 10]
    delta_mags = delta_mags[:len(lim_arc_lengths)]
    delta_mags = np.array(delta_mags)
    if delta_mags[1] < 0:
        print('Warning: first annulus has negative relative flux of value,',
              '%.5f' % delta_mags[1],
              'consider changing center or radius size')

    return (lim_arc_lengths, delta_mags, all_stds)
class RadialProfile:
    """Main function to calulate radial profiles

    Computes a radial profile of a source in an array.  This function
    leverages some of the tools in photutils to cutout the small region
    around the source.  This function can first recenter the source
    via a 2d Gaussian fit (radial profiles are sensitive to centroids)
    and then fit a 1D Moffat profile to the values.  The profile
    is calculated by computing the distance from the center of each
    pixel within a box of size r to the centroid of the source in the
    box.  Additionally, the profile and the fit can be plotted.
    If fit is set to True, then the profile is fit with a 1D Moffat.
    If show is set to True, then profile (and/or fit) is plotted.
    If an axes object is provided, the plot(s) will be on that object.
    NOTE: THE POSITIONS ARE 0 INDEXED (bottom left corner pixel
    center is set to (0,0)).


    Parameters
    ----------
    x : float
        The x position of the centroid of the source. ZERO INDEXED.
        stored in the .x attribute
    y : float
        The y position of the centroid of the source. ZERO INDEXED.
        .x attribute
    data : array
        A 2D array containing the full image data.  A small box
        is cut out of this array for the radial profile
    r : float, optional
        The size of the box used to cut out the source pixels.
        The box is typically square with side length ~ 2*r + 1.
        Default is 5 pix.
    fit:  bool
        Fit a 1D Moffat profile?  Default True.  Required for
        computation of FWHM.
    recenter : bool, optional
        Compute new centroid via 2D Gaussian fit?  Default False.
    show : bool, optional
        Plot the profile?  Default False.  See ax parameter for info.
    ax : matplotlib.axes.Axes, optional
        Axes object to make the plots on.  Default None.
        If None and show is True, an axes object will be created.

    Attributes
    ----------
    x : float
        The x position in pixels of the source centroid. Gets updated
        if the profile is recentered.
    y : float
        The y position in pixels of the source centroid. Gets updated
        if the profile is recentered.
    fwhm : float
        The FWHM of the fitted profile, only computed if fit=True
    old_x : float
        The x position in pixels of the original input centroid. Only
        set if recenter = True
    old_y : float
        The y position in pixels of the original input centroid. Only
        set if recenter = True
    fitted : bool
        Whether the data has had a profile fit. Only True if fit=True
        and fitting was successful
    is_empty : bool
        Whether cutout is empty or not.  True if position falls
        entirely off of data.
    cutout : array
        2D array containing small cutout of data around source
    distances : array
        Array containing distance to each pixel in cutout from centroid
    value : array
        Array containing all the values in the cutout
    """
    def __init__(self,
                 x,
                 y,
                 data,
                 r=5,
                 fit=True,
                 recenter=False,
                 show=False,
                 ax=None):
        self.x = x  # X position
        self.y = y  # Y Position
        self.r = r  # radius (acutally makes a box)
        self.is_empty = False  # if gets set True, cutout is empty
        self._setup_cutout(data)  # Make the cutout

        if recenter:
            self.recenter_source(data)  # recalculates centroid

        self.fit = fit
        self.fitted = False  # Initial state, set to true if fit success
        if self.is_empty:
            self.fwhm = np.nan

        else:
            self._create_profile()  # creates distances and values arrays

            if fit:
                self.fit_profile()  # performs fit, updates self.fitted
            if show:
                self.show_profile(ax)

    def _create_profile(self):
        """Compute distances to pixels in cutout"""
        iY, iX = np.mgrid[self.sy, self.sx]  # Pixel grid indices
        # extent = [sx.start, sx.stop-1, sy.start, sy.stop-1]

        self.distances = np.sqrt((iX - self.x)**2. +
                                 (iY - self.y)**2.).flatten()
        self.values = self.cutout.flatten()

    def _setup_cutout(self, data):
        """Cuts out the aperture and defines slice objects.
        General setup procedure.
        """
        self.ap = CircularAperture((self.x, self.y), r=self.r)
        mask = self.ap.to_mask()[0]
        self.sy = mask.bbox.slices[0]
        self.sx = mask.bbox.slices[1]
        self.cutout = mask.cutout(data, fill_value=np.nan)

        if self.cutout is None:
            self.is_empty = True

    def fit_profile(self):
        """Fits 1d Moffat function to measured radial profile.

        Fits a moffat profile to the distance and values of the pixels.
        Further development may allow user defined models.

        """
        try:
            amp0 = np.amax(self.values)
            bias0 = np.nanmedian(self.values)
            best_vals, covar = curve_fit(RadialProfile.profile_model,
                                         self.distances,
                                         self.values,
                                         p0=[amp0, 1.5, 1.5, bias0],
                                         bounds=([0., .3, .5, 0],
                                                 [np.inf, 10., 10., np.inf]))
            hwhm = best_vals[1] * np.sqrt(2.**(1. / best_vals[2]) - 1.)
            self.fwhm = 2 * hwhm
            self.amp, self.gamma, self.alpha, self.bias = best_vals
            self.fitted = True
            mod = RadialProfile.profile_model(self.distances, *best_vals)
            self.chisquared = chisquare(self.values, mod, ddof=4)[0]
        except Exception as e:
            print(e)
            self.amp, self.gamma, self.alpha, self.bias = [np.nan] * 4
            self.fwhm = np.nan
            self.fitted = False
            self.chisquared = np.nan

    @staticmethod
    def profile_model(r, amp, gamma, alpha, bias):
        """Returns 1D Moffat profile evaluated at r values.

        This function takes radius values and parameters in a simple 1D
        moffat profiles and returns the values of the profile at those
        radius values.  The model is defined as:
        model = amp * (1. + (r / gamma) ** 2.) ** (-1. * alpha) + bias

        Parameters
        ----------
        r : array
            The distances at which to sample the model
        amp : float
            The amplitude of the of the model
        gamma: float
            The width of the profile.
        alpha: float
            The decay of the profile.
        bias: float
            The bias level (piston term) of the data.  This is like a background
            value.

        Returns
        -------
        model : array
            The values of the model sampled at the r values.
        """
        model = amp * (1. + (r / gamma)**2.)**(-1. * alpha) + bias
        return model

    def recenter_source(self, data):
        """Recenters source position in cutout and updates x,y attributes"""

        # Archive old positions.
        self.old_x = self.x
        self.old_y = self.y

        if self.is_empty:
            self.x, self.y = np.nan, np.nan

        else:
            # Fit 2D gaussian
            xg1, yg1 = centroid_2dg(self.cutout)
            dx = xg1 + self.sx.start - self.x
            dy = yg1 + self.sy.start - self.y
            dr = (dx**2. + dy**2.)**.5
            if dr > 2.:
                print('Large shift of {},{} computed.'.format(dx, dy))
                print('Rejecting and keeping original x, y coordinates')

            else:
                self.x = xg1 + self.sx.start
                self.y = yg1 + self.sy.start
                self._setup_cutout(data)

    def show_profile(self, ax=None, show_fit=True):
        """Makes plot of radial profile

        Plots the radial profile, that is pixel distance vs
        pixel value.  Can plot on an existing axes object if
        the an axes object is passed in via the ax parameter.
        The function attempts to set sensible axes limits, specifically
        half of the smallest positive value (axes are logarithmic).
        The axes object is returned by this, so that parameters can
        be set by the user later.

        Parameters
        ----------
        ax : matplotlib.axes.Axes, optional
            An axes object to plot the radial profile on (for integrating)
            the plot into other figures.  If not set, the script will create
            an axes object.
        show_fit : bool, optional
            Plot the fitted model.  Only done if fit was successful.
        Returns
        -------
        ax : matplotlib.axes.Axes
            The axes object containing the radial profile plot/
        """
        if ax is None:
            fig = plt.figure()
            ax = fig.add_subplot(111)

        ax.scatter(self.distances, self.values, alpha=.5)
        min_y = np.amin(self.values[self.values > 0.]) / 2.
        ax.set_ylim(min_y, np.nanmax(self.values) * 2.)
        ax.set_xlim(0.)

        ax.set_yscale('log')
        ax.set_ylabel('Pixel Value')
        ax.set_xlabel('Distance from centroid [pix]')

        if self.fitted and show_fit:
            tmp_r = np.arange(0, np.ceil(np.amax(self.distances)), .1)
            model_fit = RadialProfile.profile_model(tmp_r, self.amp,
                                                    self.gamma, self.alpha,
                                                    self.bias)
            label = r'$\gamma$= {}, $\alpha$ = {}'.format(
                round(self.gamma, 2), round(self.alpha, 2))
            label += '\nFWHM = {}'.format(round(self.fwhm, 2))
            ax.plot(tmp_r, model_fit, label=label)
            ax.legend(loc=1)
        return ax