Example #1
0
 def elliptical_mask(self, scale=2):
     ell = EllipticalAperture(
         self.centroid, scale * self.semimajor_axis,
         scale * self.semiminor_axis,  np.deg2rad(self.theta)
     )
     ell_mask = ell.to_mask()[0].to_image(self.image.shape).astype(bool)
     return ell_mask
Example #2
0
    def apply_ellipse_cutout(self, array):
        """Applies an elliptical cutout to any given array according to galaxy specifications.
        
        This method uses the `astropy` Elliptical Aperture module to make an elliptical cutout
        of the galaxy by taking into account the Petrosian effective radius, inclination, and
        b/a ratio for a galaxy. A cutout from the centre of  the array is then returned with
        this requirement applied. The primary reason for working with cutouts is speed of
        analysis.
        
        Args:
            array (array): Any array, which is an emission map with a modified mask in our use.

        Returns:
            array (array): An elliptical cutout of the array 
        """

        if self.ellipical_mask_loaded == False:
            x, y = self.hamap.shape

            ellipical_aperture = EllipticalAperture(
                [(x - 1) / 2, (y - 1) / 2], np.ceil(self.eff_rad),
                np.ceil(self.eff_rad * self.elpetro_ba), self.theta)
            self.mask_ellipical = ellipical_aperture.to_mask(method='exact')[0]

            self.ellipical_mask_loaded = True

        return self.mask_ellipical.cutout(array)
    def _set_aperture_elements(self):
        """ Set internal aperture elements.
        ``self._ap_rect``, ``self.ap_el_1``, ``self.ap_el_2`` and their
        ``_in`` counterparts are always made by
        ``np.atleast_2d(self.position)``, so their results are always in
        the ``N x 2`` shape.
        """
        if hasattr(self, 'a'):
            w = self.w
            a = self.a
            b = self.b
            h = self.h
            theta = self.theta
        elif hasattr(self, 'a_in'):  # annulus
            w = self.w
            a = self.a_out
            b = self.b_out
            h = self.h_out
            theta = self.theta
        else:
            raise ValueError('Cannot determine the aperture shape.')

        # positions only accepted in the shape of (N, 2), so shape[0]
        # gives the number of positions:
        pos = np.atleast_2d(self.positions)
        self.offset = np.array([w * np.cos(theta) / 2, w * np.sin(theta) / 2])
        offsets = np.repeat(np.array([
            self.offset,
        ]), pos.shape[0], 0)

        # aperture elements for aperture,
        # OUTER aperture elements for annulus:
        self._ap_rect = RectangularAperture(positions=pos,
                                            w=w,
                                            h=h,
                                            theta=theta)
        self._ap_el_1 = EllipticalAperture(positions=pos - offsets,
                                           a=a,
                                           b=b,
                                           theta=theta)
        self._ap_el_2 = EllipticalAperture(positions=pos + offsets,
                                           a=a,
                                           b=b,
                                           theta=theta)

        if hasattr(self, 'a_in'):  # inner components of annulus
            self._ap_rect_in = RectangularAperture(positions=pos,
                                                   w=self.w,
                                                   h=self.h_in,
                                                   theta=self.theta)
            self._ap_el_1_in = EllipticalAperture(positions=pos - offsets,
                                                  a=self.a_in,
                                                  b=self.b_in,
                                                  theta=self.theta)
            self._ap_el_2_in = EllipticalAperture(positions=pos + offsets,
                                                  a=self.a_in,
                                                  b=self.b_in,
                                                  theta=self.theta)
def ap_phot_plot(xcen, ycen, image, e1, e2, theta, zp):
    apertures = EllipticalAperture([(xcen, ycen)], e1, e2, theta)
    phot_table = aperture_photometry(image, apertures)
    phot_table['aperture_sum'].info.format = '%.8g'
    fig1 = plt.figure()
    apertures.plot(color='white', lw=2)
    plt.imshow(image)
    plt.show()
    plt.close()
    mag = -2.5 * np.log10(phot_table['aperture_sum'][0]) + zp
    return phot_table['aperture_sum'][0], mag
Example #5
0
 def elliptical_mask(self, scale=2, use_sersic_model=False):
     if use_sersic_model:
         pars = self.fit_params
         ell = EllipticalAperture([pars['x_0'], pars['y_0']],
                                  scale * pars['r_eff'], scale *
                                  pars['r_eff'] * (1 - pars['ellip']),
                                  np.deg2rad(pars['theta']))
     else:
         ell = EllipticalAperture(self.centroid,
                                  scale * self.semimajor_axis,
                                  scale * self.semiminor_axis,
                                  self.theta.to('radian').value)
     ell_mask = ell.to_mask().to_image(self.image.shape).astype(bool)
     return ell_mask
Example #6
0
def radial_elliptical_aperture(position, r, elong=1., theta=0.):
    """
    Helper function given a radius, elongation and theta,
    will make an elliptical aperture.

    Parameters
    ----------
    position : tuple
        (x, y) coords for center of aperture.

    r : int or float
        Semi-major radius of the aperture.

    elong : float
        Elongation.

    theta : float
        Orientation in rad.

    Returns
    -------
    EllipticalAperture
    """
    a, b = r, r / elong
    return EllipticalAperture(position, a, b, theta=theta)
def transform_xy_ellipse(centerX, centerY, aAxis, bAxis, cubeObj):
    """ Update rectangle data widgets and image object attributes
    :param float centerX: center x coordinate
    :param float centerY: center y coordinate
    :param float a: long axis value
    :param float b: short axis value
    :param object cubeObj: current data from the cube
    :return list fValues: flux values list for each wavelength
    :return list wValues: wavelenght list for each slice
    :return Aperture_Photometry aperture: aperture of the ellipse
    """
    fValues = []
    #Because it gets all the flux on a pixel, it needs to get the area of it rather
    #the sum of it
    pixelArea = (cubeObj.cubeRAValue * 3600.) * (cubeObj.cubeDValue * 3600.)

    position = [(centerX, centerY)]
    aperture = EllipticalAperture(position, aAxis / 2, bAxis / 2)
    for i in range(cubeObj.maxSlice):
        phot_table = aperture_photometry(cubeObj.data_cube[i],
                                         aperture,
                                         method='exact')
        fValues.append(phot_table['aperture_sum'][0] * pixelArea)

    wValues = [
        ((w + 1) - cubeObj.cubeZCPix) * cubeObj.cubeWValue + cubeObj.cubeZCRVal
        for w in range(len(fValues))
    ]

    return fValues, wValues, aperture
Example #8
0
def create_aperture(aperture):
    """
    Function to create a circular or elliptical aperture.

    :param aperture: Dictionary with the aperture properties. The aperture 'type' can be
                     'circular' or 'elliptical' (str). Both types of apertures require a position,
                     'pos_x' and 'pos_y' (float), where the aperture is placed. The circular
                     aperture requires a 'radius' (in pixels, float) and the elliptical
                     aperture requires a 'semimajor' and 'semiminor' axis (in pixels, float),
                     and an 'angle' (deg). The rotation angle in degrees of the semimajor axis
                     from the positive x axis. The rotation angle increases counterclockwise.
    :type aperture: dict

    :return: Aperture object.
    :rtype: photutils.aperture.circle.CircularAperture or
            photutils.aperture.circle.EllipticalAperture
    """

    if aperture['type'] == "circular":

        phot_ap = CircularAperture((aperture['pos_x'], aperture['pos_y']),
                                   aperture['radius'])

    elif aperture['type'] == "elliptical":

        phot_ap = EllipticalAperture((aperture['pos_x'], aperture['pos_y']),
                                     aperture['semimajor'],
                                     aperture['semiminor'],
                                     math.radians(aperture['angle']))

    else:

        raise ValueError("Aperture type not recognized.")

    return phot_ap
Example #9
0
def mark_ellipse(cat, nsaid):
    pass
    # ellipse showing R24
    index = np.where(cat.NEDname == nsaid)
    #print('start of mark_ellipse',nsaid, index)
    radii = ['GAL_R24','GAL_HR17']
    colors = ['cyan','magenta']
    apertures = []
    for i,sma in enumerate(radii):
        #print('in loop',sma,index)
        rad = cat[sma][index][0]
        #print(sma, rad)
        if rad > 0.1:
            t = cat['GAL_PA'][index][0]
            if t < 0:
                theta = np.radians(180. + t+90)
            else:
                theta = np.radians(t+90) # orientation in radians

            apertures.append(EllipticalAperture((cat['GAL_XC'][index][0],cat['GAL_YC'][index][0]),\
                                            cat[sma][index][0]/pixel_scale, \
                                            cat[sma][index][0]*cat['GAL_BA'][index][0]/pixel_scale,\
                                            theta = theta))
            
    for i,aperture in enumerate(apertures):
        aperture.plot(color=colors[i],lw=1.5)
def profile_analyseplus(x,data,aperture,mask=None):
    flux=[]
    sma=aperture['sma']
    epsilon=aperture['ellipticity']
    theta=(aperture['PA']+90.)*np.pi/180.
    aperturestd = EllipticalAperture(positions=aperture['posi'],a=sma,b=sma*(1-epsilon),theta=theta)
    rawflux_table = aperture_photometry(data, aperturestd, mask=mask)
    stdflux=rawflux_table['aperture_sum'][0]
    for x1 in x:
        apertures = EllipticalAperture(positions=aperture['posi'],a=x1,b=x1*(1-epsilon),theta=theta)
        rawflux_table = aperture_photometry(data, apertures, mask=mask)
        flux.append(rawflux_table['aperture_sum'][0])
    antieerFunc=interp1d(flux,x)
    HLR=antieerFunc(0.5*stdflux)
    concentration=5*np.log(antieerFunc(0.7*stdflux)/antieerFunc(0.3*stdflux))
    result = {
        'concentration':concentration,
        'HLR':HLR,
    }
    return result
Example #11
0
    def measure_photo(self):
        """
        sample.extract(), from EllipseSample documentation:
        Extract sample data by scanning an elliptical path over the
        image array.

        Returns
        -------
        result : 2D `~numpy.ndarray`
            The rows of the array contain the angles, radii, and
            extracted intensity values, respectively.
        """
        if self.photomorph == 'ellipse':
            sample = EllipseSample(self.im,
                                   sma=self.params['sma'],
                                   astep=0,
                                   sclip=0.3,
                                   nclip=0,
                                   linear_growth=False,
                                   geometry=self.geom,
                                   integrmode='bilinear')
            self.ellip_data = sample.extract()
            self.mean_intens = np.mean(self.ellip_data[2])
        elif self.photomorph == 'annulus':
            aper = EllipticalAnnulus((self.params['x0'], self.params['y0']),
                                     self.params['sma']-self.beam_size_pix/2.,
                                     self.params['sma']+self.beam_size_pix/2.,
                                     (self.params['sma']+self.beam_size_pix/2.) \
                                     *(1. - self.params['eps']),
                                     self.params['pa'])
            annulus_mask = aper.to_mask()
            self.ellip_data = annulus_mask.multiply(self.im)
            self.sum_intens = np.sum(
                self.ellip_data) / self.beam_size_pix**2  #in Jy
            self.mean_intens = np.mean(self.ellip_data)


#            also: aperture_photometry(self.image_cube[self.chan] / self.beam_size_pix**2, aper)
        elif self.photomorph == 'circle':
            aper = CircularAperture((self.params['x0'], self.params['y0']),
                                    self.params['sma'])
            circle_mask = aper.to_mask()
            self.ellip_data = circle_mask.multiply(self.im)
            self.sum_intens = np.sum(self.ellip_data) / self.beam_size_pix**2
            self.mean_intens = np.mean(self.ellip_data)
        elif self.photomorph == 'ellipse_area':
            aper = EllipticalAperture(
                (self.params['x0'], self.params['y0']), self.params['sma'],
                self.params['sma'] * (1. - self.params['eps']),
                self.params['pa'])
            ellipse_mask = aper.to_mask()
            self.ellip_data = ellipse_mask.multiply(self.im)
            self.sum_intens = np.sum(self.ellip_data) / self.beam_size_pix**2
            self.mean_intens = np.mean(self.ellip_data)
Example #12
0
def make_blank_image(fitsimage,
                     mask_detection=True,
                     detections=None,
                     mask_aperture=2,
                     **kwargs):
    # remove all the detections, return blank image
    image = fitsimage.image.copy()
    if mask_detection:
        if detections is None:
            detections = source_finder(fitsimage, **kwargs)
        ra = np.array([detections['ra']])
        dec = np.array([detections['dec']])
        skycoords = SkyCoord(ra.flatten(), dec.flatten(), unit='deg')
        pixel_coords = np.array(
            list(zip(*skycoord_to_pixel(skycoords, fitsimage.wcs))))
        for i, coord in enumerate(pixel_coords):
            aper = EllipticalAperture(coord, mask_aperture, mask_aperture, 0)
            mask = aper.to_mask().to_image(shape=fitsimage.imagesize) > 0
            image[mask] = fitsimage.std
    return image
Example #13
0
def _fractionTotalFLuxEllipse(a: float, image: np.ndarray, b: float,
                              theta: float, centre: List[float],
                              totalsum: float) -> float:
    '''Function calculates the fraction of the total flux for the given
       elliptical parameters

       Parameters
       ----------

        a : float
            Semi-major axis or width. Must be positive

        image : np.ndarray
            image for which flux will be counted

        b : float
            Ellipses semi-minor axis or hight

        theta : float
            Rotation of ellipse w.r.t the positive x axis anticlockwise

        centre : List[float]
            centre of the ellipse.

        totalsum : float
            total flux of the object.

       Returns
       -------

       The fraction of the total flux of the object as defined by the input
       parameters minus 50% so that this function can be used to find a root at
       50% total flux : float

    '''

    apCur = EllipticalAperture(centre, a, b, theta)
    curSum = apCur.do_photometry(image, method="exact")[0][0]

    return (curSum / totalsum) - 0.5
Example #14
0
def find_centroid(data):
    """
    find the centroid again after the image was rotated
    """

    sigma_clip = SigmaClip(sigma=3., iters=10)
    bkg_estimator = MedianBackground()
    bkg = Background2D(data, (25, 25),
                       filter_size=(3, 3),
                       sigma_clip=sigma_clip,
                       bkg_estimator=bkg_estimator)

    threshold = bkg.background + (3. * bkg.background_rms)

    sigma = 2.0 * gaussian_fwhm_to_sigma  # FWHM = 2.
    kernel = Gaussian2DKernel(sigma, x_size=3, y_size=3)
    kernel.normalize()
    segm = detect_sources(data, threshold, npixels=5, filter_kernel=kernel)

    props = source_properties(data, segm)
    tbl = properties_table(props)

    my_min = 100000.

    x_shape = np.float(data.shape[0])
    y_shape = np.float(data.shape[1])

    r = 3.  # approximate isophotal extent
    apertures = []
    for prop in props:
        position = (prop.xcentroid.value, prop.ycentroid.value)
        #print(position)
        a = prop.semimajor_axis_sigma.value * r
        b = prop.semiminor_axis_sigma.value * r
        theta = prop.orientation.value
        apertures.append(EllipticalAperture(position, a, b, theta=theta))
        my_dist = np.sqrt((prop.xcentroid.value - x_shape / 2.)**2 +
                          (prop.ycentroid.value - y_shape / 2.)**2)
        if (my_dist < my_min):
            my_label = prop.id - 1
            my_min = my_dist

    mytheta = props[my_label].orientation.value
    mysize = np.int(np.round(r * props[my_label].semimajor_axis_sigma.value))
    my_x = props[my_label].xcentroid.value
    my_y = props[my_label].ycentroid.value
    return my_x, my_y, mytheta, mysize
Example #15
0
def elliptic_aperture_pix(filename,
                          position,
                          ellipticity,
                          theta,
                          r_end=9,
                          plot=True):
    #Opening FITS, getting data and header
    ima = Image(os.getcwd() + '/DATA/' + filename + '.fits')
    hdr = ima.data_header
    data = ima.data.copy()

    #Galaxy center position and position for apertures
    center = (hdr['CRPIX1'], hdr['CRPIX2'])
    position = position + center

    #Defining radius for circular apertures
    radius = 50 * np.arange(1, r_end)
    aperture = [
        EllipticalAperture(position, i, i * ellipticity, theta) for i in radius
    ]

    if plot == True:
        #Plot galaxy image with apertures
        fig, ax = plt.subplots(sharex=True, sharey=True)
        ima.data[ima.data < 0] = 0

        ima.plot(ax,
                 scale='log',
                 vmin=0,
                 vmax=0.9 * np.amax(ima.data),
                 colorbar='v')
        for i in range(len(aperture)):
            aperture[i].plot(ax, color='white', lw=2)
        ax.set_title(r'Original image - NGC 3614')
        #plt.savefig('Image_apertures', dpi = 200)

    #Table with apertures data (flux sum)
    phot_table = aperture_photometry(data, aperture)
    for col in phot_table.colnames:
        phot_table[col].info.format = '%.8g'
    print(phot_table)

    return data, position, aperture
Example #16
0
    def maximize_intens(self, par):
        if self.photomorph == 'ellipse':
            geom = EllipseGeometry(
                par[0],
                par[1],
                par[2],
                par[3],
                par[4],
            )
            sample = EllipseSample(self.im,
                                   sma=geom.sma,
                                   astep=0,
                                   sclip=0.3,
                                   nclip=0,
                                   linear_growth=False,
                                   geometry=geom,
                                   integrmode='bilinear')
            data2min = np.mean(sample.extract()[2])
        elif self.photomorph == 'annulus':
            aper = EllipticalAnnulus(
                (par[0], par[1]), par[2] - self.beam_size_pix / 2.,
                par[2] + self.beam_size_pix / 2.,
                (par[2] + self.beam_size_pix / 2.) * (1. - par[3]), par[4])
            annulus_mask = aper.to_mask()
            data2min = annulus_mask.multiply(self.im)
        elif self.photomorph == 'circle':
            aper = CircularAperture((par[0], par[1]), par[2])
            circular_mask = aper.to_mask()
            data2min = circular_mask.multiply(self.im)
        elif self.photomorph == 'ellipse_area':
            aper = EllipticalAperture((par[0], par[1]), par[2],
                                      par[2] * (1. - par[3]), par[4])
            ellipse_mask = aper.to_mask()
            data2min = ellipse_mask.multiply(self.im)

        if self.q2min == 'sum':
            tominimize = np.sum(data2min)
        elif self.q2min == 'mean':
            tominimize = np.mean(data2min)

        return -tominimize
Example #17
0
def burst_drift(npy, fslice=':', tslice=':'):

    #Load npy array
    burst = np.load(npy)

    #Subband in Frequency
    subfac = 16
    rb_sub = np.nanmean(R3H.reshape(-1, subfac, R3H.shape[1]), axis=1)

    rb_sub_zm = rb_sub[fslice, tslice]

    ynorm = (rb_sub_zm.sum(0) / np.max(rb_sub_zm.sum(0)))
    x = np.linspace(0, len(ynorm) - 1, len(ynorm))

    #Smooth

    sav = ss.savgol_filter(rb_sub_zm, 49, 6)

    #Calculate 2D ACF

    acf = ss.correlate(sav, sav)

    #Provide the initial Ellipse to be fitted
    #Calculate Ellipse Geometry
    geometry = EllipseGeometry(x0=acf.shape[1] / 2,
                               y0=acf.shape[0] / 2,
                               sma=20,
                               eps=0.4,
                               pa=60 * np.pi / 180.)
    #Show Initial Guess Ellipsee
    aper = EllipticalAperture((geometry.x0, geometry.y0), geometry.sma,
                              geometry.sma * (1 - geometry.eps), geometry.pa)

    #Plot Initial Guess Ellipse on ACF

    #plt.imshow(acf, aspect = 'auto')
    #aper.plot(color='white')

    #Fit Ellipse to 2D ACF

    ellipse = Ellipse(acf, geometry)
    isolist = ellipse.fit_image()
    model_image = build_ellipse_model(acf.shape, isolist)
    residual = acf - model_image

    fig = plt.figure(figsize=(40, 10))
    ax1 = fig.add_subplot(131)
    plt.gca().invert_yaxis()
    x = np.linspace(0, acf.shape[1] - 1, acf.shape[1])
    y = lambda x: (0.809) * x + 37.65

    plt.plot(x, y(x), color='white', label='Drift Rate = -33 MHz/ms')
    plt.imshow(sav, interpolation=None)
    plt.yticks(np.arange(0, sav.shape[0], 60), [1000, 900, 800, 700, 600])
    plt.xticks(np.arange(0, sav.shape[1], 48), [0, 2, 4, 6, 8, 10])
    plt.ylabel('Frequency (MHz)')
    plt.xlabel('Time (ms)')
    plt.title('Dynamic Spectrum R3H')
    plt.legend()

    ax2 = fig.add_subplot(132)

    plt.imshow(acf)  #, origin='lower')
    plt.ylabel('Frequency Shift (MHz)')
    plt.xlabel('Time Shfit (ms)')
    plt.yticks(np.arange(0, acf.shape[0], 79),
               [1000, 600, 200, 0, -200, -600, -1000])
    plt.xticks(np.arange(0, acf.shape[1], 49),
               [-10, -8, -6, -4, -3, 0, 2, 4, 6, 8, 10])

    #plt.plot(x, y(x))
    plt.title('2D ACF R3H')

    smas = np.linspace(10, 100, 8)
    for sma in smas:
        iso = isolist.get_closest(sma)
        x, y, = iso.sampled_coordinates()
        plt.plot(x, y, color='white')

    #ax3.imshow(model_image, origin='lower')
    #ax3.set_title('Ellipse Model')

    #ax3.imshow(residual, origin='lower')

    ax3 = fig.add_subplot(133)

    #plt.set_aspect('auto')
    x = np.linspace(0, len(ynorm) - 1, len(ynorm))
    plt.plot(x, ss.savgol_filter(ynorm, 19, 6))
    plt.title('Timeseries R3H')
    plt.ylabel('Normalized Intensity')
    plt.xlabel('Time (ms)')
    plt.xticks(np.arange(0, sav.shape[1], 48), [0, 2, 4, 6, 8, 10])
    #ax3.set_title('Residual')
    fig.savefig('Drift Rate R3H')
    return
Example #18
0
def spetial_points_calc(x0, y0, sma, eps, pa, n_points, wcs, dist_center,
                        xys_sky_PV):
    """
    Calculates the points for the three interesting points in the elliptical
    fits from which you can extract important information from. This are where
    phi is 0, 90 and 180

    Parameters
    ----------
    x0, y0, sma, eps, pa: float
        ellipse parameters
    wcs:
        wcs of the image
    n_points: int
        number of points of the pv line. The higher the more precises the
        value of the x_phis are
    dist_center: float
        this are the values set to x_phi90, the distances of the
        centers of the ellipses
    """

    vla4b_sky = SkyCoord(*mf.default_params['vla4b_deg'], unit='deg')

    ellipse_pixel, ellipse_sky = ellipse_points_calc(x0, y0, sma, eps, pa,
                                                     n_points, wcs)
    ellipse_sky_cat = concatenate(ellipse_sky)
    xys_sky_PV_cat = concatenate(xys_sky_PV)

    aper = EllipticalAperture((x0, y0), sma, sma * (1. - eps), pa)
    aper_sky = aper.to_sky(wcs)
    ell_center_sky = aper_sky.positions
    sma_arcsec = aper_sky.a.value
    sminora_arcsec = aper_sky.b.value
    xys_PV_inrange = np.array([
        xys for xys in xys_sky_PV_cat
        if (xys.separation(ell_center_sky).arcsec <= 1.1 * sma_arcsec) and (
            xys.separation(ell_center_sky).arcsec >= 0.5 * sminora_arcsec)
    ])
    xys_180_PV = np.array([
        xys for xys in xys_PV_inrange if vla4b_sky.separation(xys).arcsec <
        vla4b_sky.separation(ell_center_sky).arcsec
    ])
    xys_0_PV = np.array([
        xys for xys in xys_PV_inrange if vla4b_sky.separation(xys).arcsec >
        vla4b_sky.separation(ell_center_sky).arcsec
    ])

    distances_180 = np.array(
        [[xys.separation(ell).arcsec for ell in ellipse_sky_cat]
         for xys in xys_180_PV])
    distances_0 = np.array(
        [[xys.separation(ell).arcsec for ell in ellipse_sky_cat]
         for xys in xys_0_PV])

    idx_180 = np.where(distances_180 == np.min(distances_180))
    idx_0 = np.where(distances_0 == np.min(distances_0))

    p180_sky = ellipse_sky_cat[idx_180[1]]
    p0_sky = ellipse_sky_cat[idx_0[1]]

    xp_phi180 = p180_sky.separation(vla4b_sky).arcsec
    xp_phi90 = dist_center
    xp_phi0 = p0_sky.separation(vla4b_sky).arcsec

    return [[ellipse_pixel, ellipse_sky], [p180_sky, p0_sky],
            [xp_phi180, xp_phi90, xp_phi0]]
Example #19
0
def _prep_2dacf(time_smooth,
                freq_smooth,
                time_res,
                subband_freq_res,
                diagnostic=True):

    #Calculate 2d acf
    acf2d = ss.correlate(savts_2d_c, savsp_2d_c)

    #Cap spiked central values in acf
    cap = np.mean(
        acf2d[len(acf2d.sum(1)) // 2 + 10:len(acf2d.sum(1)) // 2 + 10,
              len(acf2d.sum(0)) // 2 + 10:len(acf2d.sum(0)) // 2 + 10])
    acf2d_cap = np.where(acf2d > cap, cap, acf2d)

    #Smooth Data w Savitzky Golay filter (t - time) (s - spectrum)
    twinlen = acf2d.shape[1] // 4
    if twinlen % 2 == 0:
        twinlen = twinlen + 1
    swinlen = acf2d.shape[0] // 4
    if swinlen % 2 == 0:
        swinlen = swinlen + 1
    polyo = 6
    savacft = ss.savgol_filter(acf2d, twinlen, polyo, axis=1)
    savacff = ss.savgol_filter(acf2d, swinlen, polyo, axis=0)
    print('ACF Time Window Length: ', twinlen)
    print('ACF Freq Window Length: ', swinlen)

    #Calculate initial guess parameters spectrum acf time
    savt = savacft.sum(0) / np.max(savacft.sum(0))
    maximt = np.max(savt)
    stdt = np.std(savt)
    meant = np.where(savt == maximt)[0][0]
    xt = np.linspace(0, len(savt), len(savt))

    #Fit 1D Gaussian to Spectrum
    fittert = modeling.fitting.LevMarLSQFitter()
    modelt = modeling.models.Gaussian1D(amplitude=maximt,
                                        mean=meant,
                                        stddev=stdt)
    fitted_modelt = fittert(modelt, xt, savt)

    #Calculate initial guess parameters spectrum acf freq
    savsp = savacff.sum(1) / np.max(savacff.sum(1))
    maximsp = np.max(savsp)
    stdsp = np.std(savsp)
    meansp = np.where(savsp == maximsp)[0][0]
    xsp = np.linspace(0, len(savsp), len(savsp))

    #Fit 1D Gaussian to Spectrum
    fittersp = modeling.fitting.LevMarLSQFitter()
    modelsp = modeling.models.Gaussian1D(amplitude=maximsp,
                                         mean=meansp,
                                         stddev=stdsp)
    fitted_modelsp = fittersp(modelsp, xsp, savsp)

    #Get Ellipse Ratio
    sigmat = fitted_modelt.stddev.value
    sigmaf = fitted_modelsp.stddev.value
    #Up to sig 3 -- MAKE EDIT TO ADJUST ITERATIVELY IN FIT FUNCTION
    sigt1 = sigmat
    sigf1 = sigmaf
    sigt2 = sigmat * 2
    sigf2 = sigmaf * 2
    sigt3 = sigmat * 3
    sigf3 = sigmaf * 3

    sigmat = sigmat * 0.3
    sigmaf = sigmaf * 0.3

    #Sigmas form a rectangle, get slope of the rectangle diagonal to estimate semi major axis PA
    hyp = np.sqrt(sigmat**2 + sigmaf**2)
    estpa = np.arccos(sigmat / hyp)  #in radians

    #Estimate ellipticity (eps) with sigma ratio
    oppestpa = np.arccos(sigmaf / hyp)
    estsmajax = np.tan(oppestpa) * (hyp / 2)
    estsminax = hyp / 2
    esteps = 1 - (estsminax / estsmajax)

    print(estsmajax, estsminax)

    print('Estimated Ellipticity: ', esteps)
    print('Estmated Semimajor Axis: ', estsmajax)
    print('Estimated PA: ', estpa)

    print('Initial guess ellipse applied!')

    #Provide the initial ellipse to be fitted
    #Calculate ellipse geometry
    geometry = EllipseGeometry(x0 = acf2d.shape[1]/2, \
      y0 = acf2d.shape[0]/2, sma = estsmajax, eps = esteps, pa = estpa)
    #Show initial guess ellipse
    aper = EllipticalAperture((geometry.x0, geometry.y0), \
      geometry.sma, geometry.sma*(1-geometry.eps),geometry.pa)

    if diagnostic == True:
        fig = plt.figure()
        plt.imshow(acf2d, aspect='auto')
        aper.plot(color='white')
        plt.title('Diagnostic - Pre-fit Estimate')
        plt.show()
        fig.savefig('Diagnostic_' + str(burst_npy) + '.png')

    print('Now for the fit...')

    return acf2d, geometry, aper, sigmat, sigmaf, sigf3
Example #20
0
b = (1. - cat.ELLIPTICITY[objectID][0]) * a

print('ellipticity = ', cat.ELLIPTICITY[objectID][0])
print('b/a = ', 1. - cat.ELLIPTICITY[objectID][0])

flux_r = np.zeros(len(a), 'f')
flux_ha = np.zeros(len(a), 'f')
if args.mask:
    maskdat = fits.getdata(args.mask)
    maskdat = np.array(maskdat, 'bool')

for i in range(len(a)):
    if args.verbose:
        print('defining elliptical aperture ' + str(i) + ' ap size = ',
              str(a[i]))
    ap = EllipticalAperture(position, a[i], b[i],
                            theta)  #,ai,bi,theta) for ai,bi in zip(a,b)]

    if args.mask:
        phot_table_r = aperture_photometry(imdat_r, ap, mask=maskdat)
        phot_table_ha = aperture_photometry(imdat_ha, ap, mask=maskdat)
    else:
        phot_table_r = aperture_photometry(imdat_r, ap)
        phot_table_ha = aperture_photometry(imdat_ha, ap)
    if args.verbose:
        print('measuring flux in aperture ' + str(i) + ' ap size = ',
              str(a[i]))
    flux_r[i] = phot_table_r['aperture_sum'][0]
    flux_ha[i] = phot_table_ha['aperture_sum'][0]

# plot image with outer ellipse
Example #21
0
def fit_ellipse_for_source(
    friendid=None,
    detectid=None,
    coords=None,
    shotid=None,
    subcont=True,
    convolve_image=False,
    pixscale=pixscale,
    imsize=imsize,
    wave_range=None,
):

    if detectid is not None:

        global deth5

        detectid_obj = detectid

        if detectid_obj <= 2190000000:
            det_info = deth5.root.Detections.read_where("detectid == detectid_obj")[0]
            linewidth = det_info["linewidth"]
            wave_obj = det_info["wave"]
            redshift = wave_obj / (1216) - 1
        else:
            det_info = conth5.root.Detections.read_where("detectid == detectid_obj")[0]
            redshift = 0
            wave_obj = 4500

        coords_obj = SkyCoord(det_info["ra"], det_info["dec"], unit="deg")

        shotid_obj = det_info["shotid"]
        fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")["fwhm_virus"][0]
        amp = det_info["multiframe"]

        if wave_range is not None:
            wave_range_obj = wave_range
        else:
            if detectid_obj <= 2190000000:
                wave_range_obj = [wave_obj - 2 * linewidth, wave_obj + 2 * linewidth]
            else:
                wave_range_obj = [4100, 4200]

        if coords is not None:
            coords_obj = coords

        if shotid is not None:
            shotid_obj = shotid
            fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[
                "fwhm_virus"
            ][0]

        try:
            hdu = make_narrowband_image(
                coords=coords_obj,
                shotid=shotid_obj,
                wave_range=wave_range_obj,
                imsize=imsize * u.arcsec,
                pixscale=pixscale * u.arcsec,
                subcont=subcont,
                convolve_image=convolve_image,
                include_error=True,
            )

        except:
            print("Could not make narrowband image for {}".format(detectid))
            return np.nan, np.nan

    elif friendid is not None:

        global friend_cat

        sel = friend_cat["friendid"] == friendid
        group = friend_cat[sel]
        coords_obj = SkyCoord(ra=group["icx"][0] * u.deg, dec=group["icy"][0] * u.deg)
        wave_obj = group["icz"][0]
        redshift = wave_obj / (1216) - 1
        linewidth = group["linewidth"][0]
        shotid_obj = group["shotid"][0]
        fwhm = group["fwhm"][0]
        amp = group["multiframe"][0]

        if wave_range is not None:
            wave_range_obj = wave_range
        else:
            wave_range_obj = [wave_obj - 2 * linewidth, wave_obj + 2 * linewidth]

        if shotid is not None:
            shotid_obj = shotid
            fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[
                "fwhm_virus"
            ][0]

        try:
            hdu = make_narrowband_image(
                coords=coords_obj,
                shotid=shotid_obj,
                wave_range=wave_range_obj,
                imsize=imsize * u.arcsec,
                pixscale=pixscale * u.arcsec,
                subcont=subcont,
                convolve_image=convolve_image,
                include_error=True,
            )
        except:
            print("Could not make narrowband image for {}".format(friendid))
            return None

    elif coords is not None:
        coords_obj = coords

        if wave_range is not None:
            wave_range_obj = wave_range
        else:
            print(
                "You need to supply wave_range=[wave_start, wave_end] for collapsed image"
            )

        if shotid is not None:
            shotid_obj = shotid
            fwhm = surveyh5.root.Survey.read_where("shotid == shotid_obj")[
                "fwhm_virus"
            ][0]
        else:
            print("Enter the shotid to use (eg. 20200123003)")

        hdu = make_narrowband_image(
            coords=coords_obj,
            shotid=shotid_obj,
            wave_range=wave_range_obj,
            imsize=imsize * u.arcsec,
            pixscale=pixscale * u.arcsec,
            subcont=subcont,
            convolve_image=convolve_image,
            include_error=True,
        )
    else:
        print("You must provide a detectid, friendid or coords/wave_range/shotid")
        return np.nan, np.nan

    w = wcs.WCS(hdu[0].header)

    if friendid is not None:

        sel_friend_group = friend_cat["friendid"] == friendid
        group = friend_cat[sel_friend_group]
        eps = 1 - group["a2"][0] / group["b2"][0]
        pa = group["pa"][0] * np.pi / 180.0 - 90
        sma = group["a"][0] * 3600 / pixscale

        coords = SkyCoord(ra=group["icx"][0] * u.deg, dec=group["icy"][0] * u.deg)
        wave_obj = group["icz"][0]
        redshift = wave_obj / (1216) - 1
        linewidth = np.nanmedian(group["linewidth"])
        shotid_obj = group["shotid"][0]
        fwhm = group["fwhm"][0]

        geometry = EllipseGeometry(
            x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=sma, eps=eps, pa=pa
        )
    else:
        geometry = EllipseGeometry(
            x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=20, eps=0.2, pa=20.0
        )

    geometry = EllipseGeometry(
        x0=w.wcs.crpix[0], y0=w.wcs.crpix[0], sma=20, eps=0.2, pa=20.0
    )
    # geometry.find_center(hdu.data)
    # aper = EllipticalAperture((geometry.x0, geometry.y0), geometry.sma,
    #                          geometry.sma*(1 - geometry.eps), geometry.pa)

    # plt.imshow(hdu.data, origin='lower')
    # aper.plot(color='white')

    ellipse = Ellipse(hdu[0].data)
    isolist = ellipse.fit_image()
    iso_tab = isolist.to_table()

    if len(iso_tab) == 0:
        geometry.find_center(hdu[0].data, verbose=False, threshold=0.5)
        ellipse = Ellipse(hdu[0].data, geometry)
        isolist = ellipse.fit_image()
        iso_tab = isolist.to_table()

    if len(iso_tab) == 0:
        return np.nan, np.nan, np.nan

        try:
            # compute iso's manually in steps of 3 pixels
            ellipse = Ellipse(hdu[0].data)  # reset ellipse
            iso_list = []
            for sma in np.arange(1, 60, 2):
                iso = ellipse.fit_isophote(sma)
                if np.isnan(iso.intens):
                    # print('break at {}'.format(sma))
                    break
                else:
                    iso_list.append(iso)
            isolist = IsophoteList(iso_list)
            iso_tab = isolist.to_table()
        except:
            return np.nan, np.nan, np.nan

    try:
        model_image = build_ellipse_model(hdu[0].data.shape, isolist)
        residual = hdu[0].data - model_image
    except:
        return np.nan, np.nan, np.nan

    sma = iso_tab["sma"] * pixscale
    const_arcsec_to_kpc = cosmo.kpc_proper_per_arcmin(redshift).value / 60.0

    def arcsec_to_kpc(sma):
        dist = const_arcsec_to_kpc * sma
        return dist

    def kpc_to_arcsec(dist):
        sma = dist / const_arcsec_to_kpc
        return sma

    dist_kpc = (
        sma * u.arcsec.to(u.arcmin) * u.arcmin * cosmo.kpc_proper_per_arcmin(redshift)
    )
    dist_arcsec = kpc_to_arcsec(dist_kpc)

    # print(shotid_obj, fwhm)
    # s_exp1d = models.Exponential1D(amplitude=0.2, tau=-50)

    alpha = 3.5
    s_moffat = models.Moffat1D(
        amplitude=1,
        gamma=(0.5 * fwhm) / np.sqrt(2 ** (1.0 / alpha) - 1.0),
        x_0=0.0,
        alpha=alpha,
        fixed={"amplitude": False, "x_0": True, "gamma": True, "alpha": True},
    )

    s_init = models.Exponential1D(amplitude=0.2, tau=-50)

    fit = fitting.LevMarLSQFitter()
    s_r = fit(s_init, dist_kpc, iso_tab["intens"])

    # Fitting can be done using the uncertainties as weights.
    # To get the standard weighting of 1/unc^2 for the case of
    # Gaussian errors, the weights to pass to the fitting are 1/unc.
    # fitted_line = fit(line_init, x, y, weights=1.0/yunc)

    # s_r = fit(s_init, dist_kpc, iso_tab['intens'])#, weights=iso_tab['intens']/iso_tab['intens_err'] )

    print(s_r)
    try:
        r_n = -1.0 * s_r.tau  # _0 #* const_arcsec_to_kpc
    except:
        r_n = np.nan  # r_n = -1. * s_r.tau_0
    try:
        sel_iso = np.where(dist_kpc >= 2 * r_n)[0][0]
    except:
        sel_iso = -1

    aper = EllipticalAperture(
        (isolist.x0[sel_iso], isolist.y0[sel_iso]),
        isolist.sma[sel_iso],
        isolist.sma[sel_iso] * (1 - isolist.eps[sel_iso]),
        isolist.pa[sel_iso],
    )

    phottable = aperture_photometry(hdu[0].data, aper, error=hdu[1].data)
    flux = phottable["aperture_sum"][0] * 10 ** -17 * u.erg / (u.cm ** 2 * u.s)
    flux_err = phottable["aperture_sum_err"][0] * 10 ** -17 * u.erg / (u.cm ** 2 * u.s)

    lum_dist = cosmo.luminosity_distance(redshift).to(u.cm)
    lum = flux * 4.0 * np.pi * lum_dist ** 2
    lum_err = flux_err * 4.0 * np.pi * lum_dist ** 2

    if detectid:
        name = detectid
    elif friendid:
        name = friendid

    # Get Image data from Elixer
    catlib = catalogs.CatalogLibrary()
    try:
        cutout = catlib.get_cutouts(
            position=coords_obj,
            side=imsize,
            aperture=None,
            dynamic=False,
            filter=["r", "g", "f606W"],
            first=True,
            allow_bad_image=False,
            allow_web=True,
        )[0]
    except:
        print("Could not get imaging for " + str(name))

    zscale = ZScaleInterval(contrast=0.5, krej=1.5)
    vmin, vmax = zscale.get_limits(values=hdu[0].data)

    fig = plt.figure(figsize=(20, 12))
    fig.suptitle(
        "{}  ra={:3.2f}, dec={:3.2f}, wave={:5.2f}, z={:3.2f}, mf={}".format(
            name, coords_obj.ra.value, coords_obj.dec.value, wave_obj, redshift, amp
        ),
        fontsize=22,
    )

    ax1 = fig.add_subplot(231, projection=w)
    plt.imshow(hdu[0].data, vmin=vmin, vmax=vmax)
    plt.xlabel("RA")
    plt.ylabel("Dec")
    plt.colorbar()
    plt.title("Image summed across 4*linewidth")

    ax2 = fig.add_subplot(232, projection=w)
    plt.imshow(model_image, vmin=vmin, vmax=vmax)
    plt.xlabel("RA")
    plt.ylabel("Dec")
    plt.colorbar()
    plt.title("model")

    ax3 = fig.add_subplot(233, projection=w)
    plt.imshow(residual, vmin=vmin, vmax=vmax)
    plt.xlabel("RA")
    plt.ylabel("Dec")
    plt.colorbar()
    plt.title("residuals (image-model)")
    # fig = plt.figure(figsize=(10,5))

    im_zscale = ZScaleInterval(contrast=0.5, krej=2.5)
    im_vmin, im_vmax = im_zscale.get_limits(values=cutout["cutout"].data)

    ax4 = fig.add_subplot(234, projection=cutout["cutout"].wcs)

    plt.imshow(
        cutout["cutout"].data,
        vmin=im_vmin,
        vmax=im_vmax,
        origin="lower",
        cmap=plt.get_cmap("gray"),
        interpolation="none",
    )

    plt.text(
        0.8,
        0.9,
        cutout["instrument"] + cutout["filter"],
        transform=ax4.transAxes,
        fontsize=20,
        color="w",
    )
    plt.contour(hdu[0].data, transform=ax4.get_transform(w))
    plt.xlabel("RA")
    plt.ylabel("Dec")
    aper.plot(
        color="white", linestyle="dashed", linewidth=2, transform=ax4.get_transform(w)
    )

    ax5 = fig.add_subplot(235)
    plt.errorbar(
        dist_kpc.value,
        iso_tab["intens"],
        yerr=iso_tab["intens_err"] * iso_tab["intens"],
        linestyle="none",
        marker="o",
        label="Lya SB profile",
    )

    plt.plot(dist_kpc, s_r(dist_kpc), color="r", label="Lya exp SB model", linewidth=2)

    plt.xlabel("Semi-major axis (kpc)")
    # plt.xlabel('Semi-major axis (arcsec)')
    plt.ylabel("Flux ({})".format(10 ** -17 * (u.erg / (u.s * u.cm ** 2))))
    plt.text(0.4, 0.7, "r_n={:3.2f}".format(r_n), transform=ax5.transAxes, fontsize=16)
    plt.text(
        0.4, 0.6, "L_lya={:3.2e}".format(lum), transform=ax5.transAxes, fontsize=16
    )
    secax = ax5.secondary_xaxis("top", functions=(kpc_to_arcsec, kpc_to_arcsec))
    secax.set_xlabel("Semi-major axis (arcsec)")
    # secax.set_xlabel('Semi-major axis (kpc)')
    plt.xlim(0, 100)

    # plt.plot(sma, s_r(sma), label='moffat psf')

    # plt.plot(dist_kpc.value, s1(kpc_to_arcsec(dist_kpc.value)),
    #        linestyle='dashed', linewidth=2,
    #         color='green', label='PSF seeing:{:3.2f}'.format(fwhm))

    # These two are the exact same
    # s1 = models.Moffat1D()
    # s1.amplitude = iso_tab['intens'][0]
    # alpha=3.5
    # s1.gamma = 0.5*(fwhm*const_arcsec_to_kpc)/ np.sqrt(2 ** (1.0 / alpha) - 1.0)
    # s1.alpha = alpha
    # plt.plot(r_1d, moffat_1d, color='orange')

    #    plt.plot(dist_kpc.value, (s1(dist_kpc.value)),
    #            linestyle='dashed', linewidth=2,
    #             color='blue', label='PSF seeing:{:3.2f}'.format(fwhm))

    E = Extract()
    E.load_shot(shotid_obj)
    moffat_psf = E.moffat_psf(seeing=fwhm, boxsize=imsize, scale=pixscale)
    moffat_shape = np.shape(moffat_psf)
    xcen = int(moffat_shape[1] / 2)
    ycen = int(moffat_shape[2] / 2)
    moffat_1d = (
        moffat_psf[0, xcen:-1, ycen] / moffat_psf[0, xcen, ycen] * iso_tab["intens"][0]
    )
    r_1d = moffat_psf[1, xcen:-1, ycen]
    E.close()

    plt.plot(
        arcsec_to_kpc(pixscale * np.arange(80)),
        iso_tab["intens"][0] * (moffat_psf[0, 80:-1, 80] / moffat_psf[0, 80, 80]),
        linestyle="dashed",
        color="green",
        label="PSF seeing:{:3.2f}".format(fwhm),
    )
    plt.legend()

    if friendid is not None:
        ax6 = fig.add_subplot(236, projection=cutout["cutout"].wcs)
        plot_friends(friendid, friend_cat, cutout, ax=ax6, label=False)
    plt.savefig("fit2d_{}.png".format(name))

    # filename = 'param_{}.txt'.format(name)
    # np.savetxt(filename, (r_n.value, lum.value))

    return r_n, lum, lum_err
Example #22
0
        time = (t1 - t0).total_seconds()
        ts.append(time)

    time = sum(ts) / 5
    rows.append((r, time))

df = pd.DataFrame(rows, columns=["r", "time"])

path = os.path.dirname(__file__)
df.to_csv(os.path.join(path, "python_aperture_size.csv"), index=False)

## Ellipse

rows = []
for r in trange(1, 201, 5):
    ap = EllipticalAperture((255.5, 255.5), r, r, 20)

    ts = []
    for i in range(5):
        t0 = datetime.now()
        t = aperture_photometry(data, ap, method="exact")
        t1 = datetime.now()
        time = (t1 - t0).total_seconds()
        ts.append(time)

    time = sum(ts) / 5
    rows.append((r, time))

df = pd.DataFrame(rows, columns=["r", "time"])

path = os.path.dirname(__file__)
Example #23
0
    def _create_apertures(self, image, image_stars_coordinates, data_shape):
        self.info('Creating apertures started')
        apertures = []
        sigma_values_table = []
        sigma_value = namedtuple('sigma_value', ['mean', 'median', 'std'])

        masks = [self._create_mask(
            star_coo, data_shape) for star_coo in image_stars_coordinates]

        for i, mask in enumerate(masks):
            mean, median, std = sigma_clipped_stats(
                image.data, mask=mask,
                sigma=self.config_section.get('sigma_clipped_sigma'),
                iters=self.config_section.get('sigma_clipped_iters'))

            self.info('Sigma clipped stats: mean={}, median={}, std={}'.format(
                mean, median, std))

            if (self.config_section.get('calculate_center') or
                    self.config_section.get('calculate_aperture')):

                star_properties = self._find_star_properties(np.copy(image.data), median,
                                                             mask, image_stars_coordinates[i])

            if self.config_section.get('calculate_center'):
                position = (star_properties['xcentroid'].value,
                            star_properties['ycentroid'].value)
            else:
                position = image_stars_coordinates[i]
                self.info(
                    'Calculate center is off, original coordinates have been used')
                self.info('Coordinates={}'.format(position))

            if self.config_section.get('calculate_aperture'):
                self.info('Calculating apertures has been started')
                a = star_properties['semimajor_axis_sigma'] * self.config_section.get(
                    'r_aperture_multi')
                b = star_properties['semiminor_axis_sigma'] * self.config_section.get(
                    'r_aperture_multi')
                theta = star_properties['orientation']
                self.info('Found aperture parameters: a={}, b={}, theta={}'.format(
                    a, b, theta))
                aperture = EllipticalAperture(
                    position, a=a.value, b=b.value, theta=theta.value)
                annulus = EllipticalAnnulus(position,
                                            a_in=a.value + self.config_section.get(
                                                'r_annulus_in'),
                                            a_out=a.value + self.config_section.get(
                                                'r_annulus_out'),
                                            b_out=b.value + self.config_section.get(
                                                'r_annulus_out'),
                                            theta=theta.value)
            else:
                self.info('Aperture calculate is off')
                self.info(
                    'Aperture paratmeters from configuration file have been used')

                aperture = CircularAperture(
                    position, r=self.config_section.get('r_aperture'))
                annulus = CircularAnnulus(position,
                                          r_in=self.config_section.get(
                                              'r_aperture') + self.config_section.get(
                                              'r_annulus_in'),
                                          r_out=self.config_section.get(
                                              'r_aperture') + self.config_section.get(
                                              'r_annulus_out'))

            apertures.append([aperture, annulus, std])
            sigma_values_table.append(sigma_value(mean, median, std))

        return apertures, sigma_values_table
def get_elliptical_aperture(data,isophote,mask=None,skyp=2):
    lenofell=len(isophote)
    sky_mean, sky_median, sky_std = sigma_clipped_stats(data, sigma=3.0, iters=5)
    datap=data-sky_median
    sky=sky_median
    DeltaSKY=0
    ellip=isophote['col6']
    ex_0=isophote['col10']
    ey_0=isophote['col12']
    ePA=isophote['col8']
    eSMA=isophote['col1']
    fluxList=[]
    aperarea=[]
    for loop in range(lenofell):
        a=eSMA[loop]
        b=a*( 1 - (ellip[loop]) )
        posi = [ex_0[loop], ey_0[loop]]
        apertures = EllipticalAperture(positions=posi,a=a,b=b,theta=(ePA[loop]+90))
        rawflux_table = aperture_photometry(datap, apertures, mask=mask)
        aperturesMaskedArea = aperture_photometry(mask.astype(int), apertures)['aperture_sum']
        aperarea.append((apertures.area()-aperturesMaskedArea[0]))
        fluxList.append(rawflux_table['aperture_sum'][0])
        print ("{0} percent done".format(100*loop/lenofell) )
    for loop in range(skyp) :
        antieerFunc=interp1d(fluxList,eSMA)
        aper1=1.4*antieerFunc(0.9*fluxList[lenofell-1])
        deltaSKY=0
        mass=0
        if(aper1 < 0.5*eSMA[lenofell-1]):
            aper1=0.5*eSMA[lenofell-1]
        if(aper1 > 0.75*eSMA[lenofell-1]):
            aper1=0.5*eSMA[lenofell-1]
        bignumber=0
        for loop2 in range(lenofell) :
            if eSMA[loop2] > aper1 :
                bignumber=loop2
                break
        loop3=bignumber+1
        while (loop3 < lenofell) :
            deltaSKy=(fluxList[loop3]-fluxList[bignumber])/(aperarea[loop3]-aperarea[bignumber])
            deltaSKY += deltaSKy*(pow(loop3,3))
            loop3+=1
            mass += pow(loop3,3)
        Dsky=deltaSKY/mass
        DeltaSKY += 0.3*Dsky
        for loop3 in range(lenofell) :
            fluxList[loop3] += (-0.3)*Dsky*(aperarea[loop3])
        print ("{0} percent done".format(100*loop/lenofell) )
        fig = plt.figure(figsize=(8, 6))
        plt.plot(eSMA, fluxList, label='Curve of Growth')
        plt.axvline(x=1.4*antieerFunc(0.95*fluxList[lenofell-1]), linestyle='--', color='b',   label='standard aperture')
        plt.xlabel('Semi-major axis length [pixel]', fontsize=16)
        plt.legend(loc='lower right')
        plt.show()
    sky += DeltaSKY
    antieerFunc=interp1d(fluxList,eSMA)
    for loop in range(lenofell) :
        if eSMA[loop] > (1.4*antieerFunc(0.95*fluxList[lenofell-1])) :
            std2=loop
            break
    a2=eSMA[std2]
    b2=a2*( 1 - (ellip[std2]) )
    posi2 = [ex_0[std2], ey_0[std2]]
    aperturestd = EllipticalAperture(positions=posi2,a=a2,b=b2,theta=(ePA[std2]+90))
    stdflux=fluxList[std2]
    plt.figure(figsize=(10, 10))
    norm = ImageNormalize(stretch=LogStretch())
    plt.imshow(data, cmap='Greys', origin='lower', norm=norm)
    ax = plt.gca()
    ellipseAp = patches.Ellipse(xy=posi2,width=2*a2,height=2*b2,angle=(ePA[std2]+90),color='r',fill=False)
    ax.add_patch(ellipseAp)
    plt.show()
    result = {
        'aperture': aperturestd ,
        'flux' : stdflux ,
        'sky': sky,
        'fluxList': fluxList,
        'stdn':std2
    }
    return result
Example #25
0
def do_aper_phot(data, catalog, extname, ivar_adu):
    # catalog should be the catalog with refined centroids
    # for **one CI camera**

    print('Attempting to do aperture photometry')
    positions = list(zip(catalog['xcentroid'], catalog['ycentroid']))

    radii = aper_rad_pix(extname)

    apertures = [CircularAperture(positions, r=r) for r in radii]
    annulus_apertures = CircularAnnulus(positions, r_in=60.0, r_out=65.0)
    annulus_masks = annulus_apertures.to_mask(method='center')

    par = common.ci_misc_params()

    b_over_a = (1.0 if (extname == 'CIC') else par['nominal_mer_cd'] /
                par['nominal_sag_cd'])

    # the long axis of elliptical aperture (in terms of pixels) needs to
    # be in the CI pixel Y direction
    apertures_ell = [
        EllipticalAperture(positions, a, a * b_over_a, theta=np.pi / 2)
        for a in radii
    ]

    # 107 um fiber diam, 9 um on a side for a pixel
    # fiber diam from Table 4.1 of https://arxiv.org/abs/1611.00037
    rad_fiber_pix_sag = (107.0 / 9.0) / 2.0
    deg_to_normal = 5.43  # [desi-commiss 522]
    if extname != 'CIC':
        rad_fiber_pix_mer = rad_fiber_pix_sag * np.sin(deg_to_normal /
                                                       (180.0 / np.pi))
    else:
        rad_fiber_pix_mer = rad_fiber_pix_sag

    aper_fib = EllipticalAperture(positions,
                                  rad_fiber_pix_sag,
                                  rad_fiber_pix_mer,
                                  theta=np.pi / 2)

    bkg_median = []
    for mask in annulus_masks:
        annulus_data = mask.multiply(data)
        annulus_data_1d = annulus_data[mask.data > 0]
        # this sigma_clipped_stats call is actually the slow part !!
        _, median_sigclip, std_bg = sigma_clipped_stats(annulus_data_1d)
        bkg_median.append(median_sigclip)

    bkg_median = np.array(bkg_median)
    phot = aperture_photometry(data,
                               apertures,
                               error=aper_phot_unc_map(ivar_adu))

    for i, aperture in enumerate(apertures):
        aper_bkg_tot = bkg_median * _get_area_from_ap(aperture)
        catalog['aper_sum_bkgsub_' +
                str(i)] = phot['aperture_sum_' + str(i)] - aper_bkg_tot

        catalog['aper_bkg_' + str(i)] = aper_bkg_tot
        catalog['aperture_sum_err_' + str(i)] = phot['aperture_sum_err_' +
                                                     str(i)]

    ###
    del phot
    phot = aperture_photometry(data,
                               apertures_ell,
                               error=aper_phot_unc_map(ivar_adu))
    for i, aperture in enumerate(apertures_ell):
        aper_bkg_tot = bkg_median * _get_area_from_ap(aperture)
        catalog['aper_ell_sum_bkgsub_' +
                str(i)] = phot['aperture_sum_' + str(i)] - aper_bkg_tot

        catalog['aper_ell_bkg_' + str(i)] = aper_bkg_tot
        catalog['aperture_ell_sum_err_' + str(i)] = phot['aperture_sum_err_' +
                                                         str(i)]
    ###

    ###
    del phot
    phot = aperture_photometry(data,
                               aper_fib,
                               error=aper_phot_unc_map(ivar_adu))

    aper_bkg_tot = bkg_median * _get_area_from_ap(aper_fib)
    catalog['aper_sum_bkgsub_fib'] = phot['aperture_sum'] - aper_bkg_tot

    catalog['aper_bkg_fib'] = aper_bkg_tot
    catalog['aperture_sum_err_fib'] = phot['aperture_sum_err']

    ####

    # is .area() result a vector or scalar ??
    catalog['sky_annulus_area_pix'] = _get_area_from_ap(annulus_apertures)
    catalog['sky_annulus_median'] = bkg_median
Example #26
0
def mask_obj(img, snr=3.0, exp_sz= 1.2, plt_show = True):
    threshold = detect_threshold(img, snr=snr)
    center_img = len(img)/2
    sigma = 3.0 * gaussian_fwhm_to_sigma# FWHM = 3.
    kernel = Gaussian2DKernel(sigma, x_size=5, y_size=5)
    kernel.normalize()
    segm = detect_sources(img, threshold, npixels=10, filter_kernel=kernel)
    npixels = 20
    segm_deblend = deblend_sources(img, segm, npixels=npixels,
                                    filter_kernel=kernel, nlevels=15,
                                    contrast=0.001)
    #Number of objects segm_deblend.data.max()
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12.5, 10))
    import copy, matplotlib
    my_cmap = copy.copy(matplotlib.cm.get_cmap('gist_heat')) # copy the default cmap
    my_cmap.set_bad('black')
    vmin = 1.e-3
    vmax = 2.1 
    ax1.imshow(img, origin='lower', cmap=my_cmap, norm=LogNorm(), vmin=vmin, vmax=vmax)
    ax1.set_title('Data')
    ax2.imshow(segm_deblend, origin='lower', cmap=segm_deblend.cmap(random_state=12345))
    ax2.set_title('Segmentation Image')
    plt.close()
    columns = ['id', 'xcentroid', 'ycentroid', 'source_sum', 'area']
    cat = source_properties(img, segm_deblend)
    tbl = cat.to_table(columns=columns)
    tbl['xcentroid'].info.format = '.2f'  # optional format
    tbl['ycentroid'].info.format = '.2f'
    print(tbl)
    
    from photutils import EllipticalAperture
    cat = source_properties(img, segm_deblend)
    segm_deblend_size = segm_deblend.areas
    apertures = []
    for obj in cat:
        size = segm_deblend_size[obj.id]
        print 'obj.id', obj.id
        position = (obj.xcentroid.value, obj.ycentroid.value)
        a_o = obj.semimajor_axis_sigma.value
        b_o = obj.semiminor_axis_sigma.value
        size_o = np.pi * a_o * b_o
        r = np.sqrt(size/size_o)*exp_sz
        a, b = a_o*r, b_o*r
        theta = obj.orientation.value
        apertures.append(EllipticalAperture(position, a, b, theta=theta))
    
    dis_sq = [np.sqrt((apertures[i].positions[0][0]-center_img)**2+(apertures[i].positions[0][1]-center_img)**2) for i in range(len(apertures))]
    dis_sq = np.asarray(dis_sq)
    c_index= np.where(dis_sq == dis_sq.min())[0][0]
    #from astropy.visualization.mpl_normalize import ImageNormalize
    #norm = ImageNormalize(stretch=SqrtStretch())
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12.5, 10))
    ax1.imshow(img, origin='lower', cmap=my_cmap, norm=LogNorm(), vmin=vmin, vmax=vmax)
    ax1.set_title('Data')
    ax2.imshow(segm_deblend, origin='lower',
               cmap=segm_deblend.cmap(random_state=12345))
    ax2.set_title('Segmentation Image')
    for i in range(len(apertures)):
        aperture = apertures[i]
        aperture.plot(color='white', lw=1.5, ax=ax1)
        aperture.plot(color='white', lw=1.5, ax=ax2)
    if plt_show == True:
        plt.show()
    else:
        plt.close()
    
    from regions import PixCoord, EllipsePixelRegion
    from astropy.coordinates import Angle
    
    obj_masks = []  # In the script, the objects are 1, emptys are 0.
    for i in range(len(apertures)):
        aperture = apertures[i]
        x, y = aperture.positions[0]
        center = PixCoord(x=x, y=y)
        theta = Angle(aperture.theta/np.pi*180.,'deg')
        reg = EllipsePixelRegion(center=center, width=aperture.a*2, height=aperture.b*2, angle=theta)
        patch = reg.as_artist(facecolor='none', edgecolor='red', lw=2)
        fig, axi = plt.subplots(1, 1, figsize=(10, 12.5))
        axi.add_patch(patch)
        mask_set = reg.to_mask(mode='center')
        mask = mask_set.to_image((len(img),len(img)))
        axi.imshow(mask, origin='lower')
        plt.close()
        obj_masks.append(mask)
    return obj_masks
def ap_phot(xcen, ycen, image, e1, e2, theta, zp):
    apertures = EllipticalAperture([(xcen, ycen)], e1, e2, theta)
    phot_table = aperture_photometry(image, apertures)
    phot_table['aperture_sum'].info.format = '%.8g'
    mag = -2.5 * np.log10(phot_table['aperture_sum'][0]) + zp
    return phot_table['aperture_sum'][0], mag
Example #28
0
    def plot_ellipse(self, first_time):
        self.ax_text.clear()
        if not first_time:
            self.ax.patches[1].remove()
        else:
            pass
        if self.photomorph == 'ellipse':
            aper = EllipticalAperture(
                (self.params['x0'], self.params['y0']), self.params['sma'],
                self.params['sma'] * (1. - self.params['eps']),
                self.params['pa'])
            self.ax_text.text(
                0.,
                0.5,
                r"Mean intens path: {:.3} Jy/beam".format(self.mean_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
        elif self.photomorph == 'annulus':
            aper = EllipticalAnnulus((self.params['x0'], self.params['y0']),
                                     self.params['sma']-self.beam_size_pix/2.,
                                     self.params['sma']+self.beam_size_pix/2.,
                                     (self.params['sma']+self.beam_size_pix/2.) \
                                     *(1. - self.params['eps']),
                                     self.params['pa'])
            self.ax_text.text(
                0.,
                0.5,
                r"Flux density annulus: {:.3} Jy".format(self.sum_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
            self.ax_text.text(
                0.,
                1,
                r"Mean intens path: {:.3} Jy/beam".format(self.mean_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
        elif self.photomorph == 'circle':
            aper = CircularAperture((self.params['x0'], self.params['y0']),
                                    self.params['sma'])
            self.ax_text.text(
                0.,
                0.5,
                r"Flux density circle: {:.3} Jy".format(self.sum_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
            self.ax_text.text(
                0.,
                1,
                r"Mean intens: {:.3} Jy/beam".format(self.mean_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
        elif self.photomorph == 'ellipse_area':
            aper = EllipticalAperture(
                (self.params['x0'], self.params['y0']), self.params['sma'],
                self.params['sma'] * (1. - self.params['eps']),
                self.params['pa'])
            self.ax_text.text(
                0.,
                0.5,
                r"Flux density circle: {:.3} Jy".format(self.sum_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )
            self.ax_text.text(
                0.,
                1,
                r"Mean intens: {:.3} Jy/beam".format(self.mean_intens),
                fontsize=10,
                color='k',
                verticalalignment='top',
                transform=self.ax_text.transAxes,
            )

        aper.plot(self.ax, color=self.color_ref_ellip)

        self.ax_text.set_axis_off()
        self.color_ref_ellip = 'r'
Example #29
0
def segmentation_photometry(path_file_abs,
                            bkg_sigma=3.0,
                            source_snr=3.0,
                            fwhm_kernel=2.0,
                            x_size_kernel=3,
                            y_size_kernel=3,
                            clobber=False):
    """
    aperture photometry from source segmentation
    make_source_mask not yet available in photutils v0.2.2, this version
    manually creates a source mask for determining background

    """

    import os
    import copy
    import glob
    import pickle
    import numpy as np
    from scipy import ndimage
    import matplotlib
    #matplotlib.rcParams['text.usetex'] = True
    #matplotlib.rcParams['text.latex.unicode'] = True
    #from matplotlib.backends.backend_pdf import PdfPages
    import matplotlib.pyplot as plt
    from astropy.io import fits, ascii
    from astropy.convolution import Gaussian2DKernel
    from astropy.stats import sigma_clipped_stats, gaussian_fwhm_to_sigma
    #    from astropy.table import Table
    from astropy.visualization import (LogStretch, mpl_normalize)
    #    from astropy.extern.six.moves import StringIO
    from photutils import (detect_threshold, EllipticalAperture,
                           source_properties, properties_table)
    from photutils.detection import detect_sources
    from photutils.utils import random_cmap

    # create preliminary mask
    #from photutils import make_source_mask
    #masterMask = make_source_mask(master, snr=2, npixels=5, dilate_size=11)

    #        if LEDoff was used, get threshold from LEDoff/background
    #        path_dataset = os.path.dirname(path_file_abs) + os.path.sep
    #        filenameCombined = '\t'.join(
    #            os.listdir(os.path.join(datasetDirLocal, 'master')))
    #        if 'master_ledoff_subtracted' in filename:
    #            print('Using master_ledoff')
    #            # path_file_abs = os.path.join(datasetDir, 'master', filename)
    #            hdu = fits.open(path_file_abs)[0]
    #            data_subtracted = hdu.data
    #            # calculate threadhold
    #            ledoff_pred = np.mean(data_subtracted) * \
    #                np.ones(data_subtracted.shape)
    #            mse = mean_squared_error(data_subtracted, ledoff_pred)
    #            rmse = np.sqrt(mse)
    #            threshold = 7.0 * rmse
    #            threshold_value = threshold
    #         if no LEDoff was used, background subtraction is needed
    #         there should exist no file named "subtracted"
    #         if 'master.fit' in filenameCombined \
    #             or 'master_normalised.fit' in filenameCombined:

    #filenamedir = os.path.basename(path_file_abs)
    #print(filenamedir)
    #New stuff made by Parker
    f_dir, filename = os.path.split(path_file_abs)
    ff = os.path.splitext(filename)[0]
    new_dir = f_dir + '/' + ff

    if not os.path.exists(new_dir):
        os.makedirs(new_dir)
    dir_save = new_dir
    print("The photometry files will be saved in ", dir_save)
    filenames_combined = '\t'.join(os.listdir(dir_save))

    if clobber == False \
        and 'segm.obj' in filenames_combined  \
        and 'props.obj' in filenames_combined \
        and 'props.csv' in filenames_combined\
        and 'props.ecsv' in filenames_combined:
        print('Photometry properties table already exists. Reading objects...')
        segm = pickle.load(
            open(glob.glob(os.path.join(dir_save, '*segm.obj*'))[0], 'rb'))
        props = pickle.load(
            open(glob.glob(os.path.join(dir_save, '*props.obj*'))[0], 'rb'))

        return [segm, props]

    if 'master' in path_file_abs:
        if 'normalised' in path_file_abs:
            print('Performing photometry to ' +
                  'normalised master object image {}...'.format(path_file_abs))
        else:
            print('Performing photometry to ' +
                  'un-normalised master image {}...'.format(path_file_abs))
    else:
        print('Warning: Photometry being performed to ' +
              'a single exposure {}...'.format(path_file_abs))

    hdu = fits.open(path_file_abs)[0]
    data = hdu.data
    header = hdu.header

    if 'EXPREQ' in header:
        exptime = header['EXPREQ']
    elif 'EXPTIME' in header:
        exptime = header['EXPTIME']
    else:
        print('Exposure time not found in header. Cannot determine magnitude.')
        exptime = np.nan

    # === Iteratively determine background level ===

    # assuming background is homogenous, estimate background by sigma clipping
    # if background noise varies across image, generate 2D background instead
    print('Determining background noise level...' '')
    [mean, median, std] = sigma_clipped_stats(data, sigma=bkg_sigma, iters=5)
    threshold = median + (std * 2.0)
    segm = detect_sources(data, threshold, npixels=5)
    # turn segm into a mask
    mask = segm.data.astype(np.bool)
    # dilate the source mask to ensure complete masking of detected sources
    dilate_structure = np.ones((5, 5))
    mask_dilated = ndimage.binary_dilation(mask, structure=dilate_structure)
    # get sigma clipping stats of background, without sources that are masekd
    [bkg_mean, bkg_median, bkg_std] = sigma_clipped_stats(data,
                                                          sigma=bkg_sigma,
                                                          mask=mask_dilated,
                                                          iters=3)

    # === Detect sources by segmentation ===

    print('Determining threshold for source detection...')
    # determine threshold for source detection
    # in current implementation, if all inputs are present, the formula is
    # threshold = background + (background_error * snr)
    threshold = detect_threshold(data,
                                 background=bkg_median,
                                 error=bkg_std,
                                 snr=source_snr)
    print('Preparing 2D Gaussian kernal...')
    sigma_kernel = fwhm_kernel * gaussian_fwhm_to_sigma
    kernel = Gaussian2DKernel(sigma_kernel,
                              x_size=x_size_kernel,
                              y_size=y_size_kernel)
    # normalise kernel
    # The kernel models are normalized per default, ∫∞−∞f(x)dx=1∫−∞∞f(x)dx=1.
    # But because of the limited kernel array size, the normalization
    # for kernels with an infinite response can differ from one.
    kernel.normalize()
    # obtain a  SegmentationImage object with the same shape as the data,
    # where sources are labeled by different positive integer values.
    # A value of zero is always reserved for the background.
    # if the threshold includes the background level as above, then the image
    # input into detect_sources() should not be background subtracted.
    print('Segmentation processing...')
    segm = detect_sources(data, threshold, npixels=5, filter_kernel=kernel)
    print('Segmentation labels are: ', repr(segm.labels))

    # === Measure regional source properties ===

    # source_properties() assumes that the data have been background-subtracted.
    # Background is the background level that was previously present
    # in the input data.
    # The input background does not get subtracted from the input data,
    # which should already be background-subtracted.
    print('Extracting source properties...')
    props = source_properties(data - bkg_median, segm, background=bkg_median)
    # add flux and instrumental magnitude to properties
    # flux = source_sum / exptime
    # instrumental magnitude = -2.5 * log10(flux)
    for i in range(len(props)):
        # source_sum is by definition background-subtracted already
        props[i].flux = props[i].source_sum / exptime
        props[i].mag_instr = -2.5 * np.log10(props[i].flux)
    # make plots and save to images
    # define approximate isophotal ellipses for each object
    apertures = []
    r = 2.8  # approximate isophotal extent
    for prop in props:
        position = (prop.xcentroid.value, prop.ycentroid.value)
        a = prop.semimajor_axis_sigma.value * r
        b = prop.semiminor_axis_sigma.value * r
        theta = prop.orientation.value
        apertures.append(EllipticalAperture(position, a, b, theta=theta))

    # create a table of properties
    try:
        props_table = properties_table(props)
    except:
        print('No source detected in {}'.format(path_file_abs))
        return [None, None]

    props_table['flux'] = [props[i].flux for i in range(len(props))]
    props_table['mag_instr'] = [props[i].mag_instr for i in range(len(props))]
    # add custom columns to the table: mag_instru and flux

    # plot centroid and segmentation using approximate elliptical apertures
    norm = mpl_normalize.ImageNormalize(stretch=LogStretch())
    rand_cmap = random_cmap(segm.max + 1, random_state=12345)
    #[fig1, (ax1, ax2)] = plt.subplots(1, 2, figsize = (12, 6))
    #ax1.imshow(data, origin='lower', cmap=plt.cm.gray, norm=norm)
    #ax1.plot(
    #        props_table['xcentroid'], props_table['ycentroid'],
    #        ls='none', color='blue', marker='+', ms=10, lw=1.5)
    #ax2.imshow(segm, origin='lower', cmap=rand_cmap)
    #for aperture in apertures:
    #    aperture.plot(ax=ax1, lw=1.0, alpha=1.0, color='red')
    #    aperture.plot(ax=ax2, lw=1.0, alpha=1.0, color='red')
    # plot using actual segmentation outlines (to be improved)
    #[fig2, ax3] = plt.subplots(figsize = (6, 6))
    #ax3.imshow(data, origin='lower', cmap=plt.cm.gray, norm=norm)
    #segm_outline = np.array(segm.outline_segments(), dtype=float)
    #segm_outline[segm_outline<1] = np.nan
    # get a copy of the gray color map
    #segm_outline_cmap = copy.copy(plt.cm.get_cmap('autumn'))
    # set how the colormap handles 'bad' values
    #segm_outline_cmap.set_bad(alpha=0)
    #ax3.imshow(segm_outline, origin='lower', cmap=segm_outline_cmap)

    # === save ===
    # Save segm, porps to object files, and also save props to table file.
    print('Saving segmentation and source propdderties to {}...'.format(
        dir_save))
    try:
        # if filename ends with fits, remove it in the  filename
        if filename[-5:] == '.fits':
            dir_save_prefix = os.path.join(dir_save, filename[0:-5])
        else:
            dir_save_prefix = os.path.join(dir_save, filename)
        # Enhanced CSV allows preserving table meta-data such as
        # column data types and units.
        # In this way a data table can be stored and read back as ASCII
        # with no loss of information.
        ascii.write(props_table,
                    dir_save_prefix + '-phot_props.ecsv',
                    format='ecsv')
        # csv for readability in MS excel
        ascii.write(props_table,
                    dir_save_prefix + '-phot_props.csv',
                    format='csv')

        # dump segmentation and properties to object files in binary mode
        file_segm = open(dir_save_prefix + '-phot_segm.obj', 'wb')
        pickle.dump(segm, file_segm)
        file_props = open(dir_save_prefix + '-phot_props.obj', 'wb')
        pickle.dump(props, file_props)

        # save figures
        #fig1.savefig(dir_save_prefix + '-phot_segm_fig1.png', dpi=600)
        #pp1 = PdfPages(dir_save_prefix + '-phot_segm_fig1.pdf')
        #pp1.savefig(fig1)
        #pp1.close()

        #fig2.savefig(dir_save_prefix + '-phot_segm_fig2.png', dpi=600)
        #pp2 = PdfPages(dir_save_prefix + '-phot_segm_fig2.pdf')
        #pp2.savefig(fig2)
        #pp2.close()

        print('Segmentation, properties objects, tables, and images saved to',
              dir_save)
    except:
        print('Unable to write to disk, check permissions.')

    return [segm, props]
Example #30
0
def isophote_data() :
    
    from photutils.isophote import EllipseGeometry
    from photutils import EllipticalAperture
    from photutils.isophote import Ellipse
    
    test = 'a2744/cutouts/a2744_ID_5830_f160w.fits'
    (data, dim, photfnu, R_e,
     redshift, sma, smb, pa) = open_cutout(test)
    (noise, _, _, _,
     _, _, _, _) = open_cutout('a2744/cutouts/a2744_ID_5830_f160w_noise.fits')
    plt.display_image_simple(data, cmap=cm.viridis)
    
    xlen, ylen = dim[1], dim[0]
    
    geometry = EllipseGeometry(x0=xlen/2, y0=ylen/2, sma=20, eps=0.5,
                               pa=70*np.pi/180)
    aper = EllipticalAperture((geometry.x0, geometry.y0), geometry.sma,
                               geometry.sma*(1 - geometry.eps), geometry.pa)
    
    # pyp.imshow(data, origin='lower')
    # aper.plot(color='white')
    
    ellipse = Ellipse(data, geometry)
    isolist = ellipse.fit_image()
    
    # print(isolist.tflux_e)
    
    isophotes = True
    if isophotes :
        plt.display_isophotes(data, isolist, cmap=cm.viridis)
        
        # from photutils.isophote import build_ellipse_model
        # model = build_ellipse_model(data.shape, isolist)
        # residual = data - model
        # plt.display_image_simple(residual, norm=None)
    
    annuli = True
    if annuli :
        from photutils import EllipticalAnnulus
        from photutils import aperture_photometry
        
        center = (isolist[0].x0, isolist[0].y0)
        # print(center)
        
        last = np.where(isolist.stop_code == 0)[0][-1]
        isolist = isolist[last]
        
        pa = isolist.pa
        # print(pa*180/np.pi)
        
        a_outs = np.arange(1e-5, isolist.sma, isolist.sma/11)
        b_outs = a_outs*(1-isolist.eps)
        
        for i in range(len(a_outs) - 1) :
            a_in = a_outs[i]
            a_out = a_outs[i+1]
            b_in = b_outs[i]
            b_out = b_outs[i+1]
            # print(a_in, a_out, b_in, b_out)
            aper = EllipticalAnnulus(center, a_in, a_out, b_out, b_in=b_in,
                                     theta=isolist.pa)
            phot_table = aperture_photometry(data, aper, error=noise)
            flux = phot_table['aperture_sum'][0]
            flux_err = phot_table['aperture_sum_err'][0]
            # print(flux)
            # print(flux_err)
            
            annulus_mask = aper.to_mask()
            
            annulus_data = annulus_mask.multiply(data)
            # plt.display_image_simple(annulus_data, cmap=cm.viridis, norm=None)
            # print(np.sum(annulus_data))
            
            err_table = aperture_photometry(noise, aper)
            flux_err_alt = err_table['aperture_sum'][0]
            # print(flux_err)
            
            err_data = annulus_mask.multiply(noise)
            # print(np.sum(err_data))
            
            # print(flux/flux_err)
            print(flux/flux_err_alt)
            # print()
            
    return
Example #31
0
def segmentation_photometry(path_image_abs,
                            path_error_abs = None,
                            logger = None,
                            bkg_sigma = 1.5,
                            source_snr = 1.05,
                            fwhm_kernel = 25,
                            x_size_kernel = 100,
                            y_size_kernel = 80,
                            dump_pickle = False,
                            clobber = True):

    """

    given a fits file (master image), this function calculates
    photometry by source segmentation.

    make_source_mask not yet available in photutils v0.2.2, this version
    manually creates a source mask for determining background.

    """
    
    def msg(string, msgtype = None):
        
        if logger == None:
            print(string)
        else:
            print(string)
            if msgtype == 'info':
                logger.info(string)
            if msgtype == 'error':
                logger.error(string)
            if msgtype == 'warning':
                logger.warning(string)   

    filename = os.path.basename(path_image_abs)
    dir_save = os.path.dirname(path_image_abs)
    filenames_combined = '\t'.join(os.listdir(dir_save))

    if clobber == False \
        and filename[0:-5]+'-segm.obj' in filenames_combined  \
        and filename[0:-5]+'-props.obj' in filenames_combined \
        and filename[0:-5]+'-centroid_outline.png' in filenames_combined \
        and filename[0:-5]+'-centroid_outline.pdf' in filenames_combined \
        and filename[0:-5]+'-segmentation.png' in filenames_combined \
        and filename[0:-5]+'-segmentation.pdf' in filenames_combined:
        msg('Photometry properties table already exists. '
            + 'Reading pickles...', 
            msgtype='info')

        try:
            segm = pickle.load(open(glob.glob(os.path.join(
                                dir_save, filename[0:-5]+'-segm.obj*'))[0],
                                'rb'))
            props_list = pickle.load(open(glob.glob(os.path.join(
                                dir_save, filename[0:-5]+'-props.obj*'))[0],
                                'rb'))
            return [segm, props_list]
        except:
            # pickle file corrupt or empty, proceed
            pass
    elif clobber == False \
        and filename[0:-5]+'-logstretch.png' in filenames_combined \
        and filename[0:-5]+'-logstretch.pdf' in filenames_combined:
        msg('Non-detection from previous results.',
            msgtype='info')
        return [None, []]
        
    # image type notifications
    if 'master' in path_image_abs:
        if 'normalised' in path_image_abs:
            msg('Performing photometry to '
                + 'normalised master object image {}...'.format(path_image_abs),
                msgtype='info')
        else:
            msg('Performing photometry to '
                + 'un-normalised master image {}...'.format(path_image_abs),
                msgtype='info')
    elif 'reduced' in path_image_abs:
        msg('Performing photometry to '
                + 'reduced image frame {}...'.format(path_image_abs),
                msgtype='info')
    else:
        msg('Warning: Photometry being performed to '
            + 'a single exposure {}...'.format(path_image_abs),
            msgtype='warning')
    
    # read in data
    try:
        hdu = fits.open(path_image_abs)['FPC']
        data = hdu.data
    except:
        hdu = fits.open(path_image_abs)[0]
        data = hdu.data
    # read in error in data
    msg('Reading master error image {}...'
        .format(path_error_abs))
    try:
        hdu_error = fits.open(path_error_abs)[0]
        data_error = hdu_error.data
    except:
        data_error = np.zeros(data.shape)
        msg('No master error image available for {}'
            .format(path_image_abs))

    header = hdu.header

    if 'EXPREQ' in header:
        exptime = header['EXPREQ']
    elif 'EXPTIME' in header:
        exptime = header['EXPTIME']
    else:
        msg('Exposure time not found in header. '
            + 'Cannot determine magnitude.',
            msgtype='error')
        exptime = np.nan

    # === Iteratively determine background level ===

    # assuming backcground is homogenous, estimate background by sigma clipping
    # if background noise varies across image, generate 2D background instead
    # using the Background function
    msg('Determining background noise level...',
        msgtype='info')
    [mean, median, std] = sigma_clipped_stats(data, sigma=bkg_sigma, iters=3)
    threshold = median + (std * 4)
    segm = detect_sources(data, threshold, npixels=5)
    # turn segm into a mask
    mask = segm.data.astype(np.bool)
    # dilate the source mask to ensure complete masking of detected sources
    dilate_structure = np.ones((5, 5))
    mask_dilated = ndimage.binary_dilation(mask, structure=dilate_structure)
    # get sigma clipping stats of background, without sources that are masekd
    [bkg_mean, bkg_median, bkg_std] = sigma_clipped_stats(
        data, sigma=bkg_sigma, mask=mask_dilated, iters = 3)

    # === Detect sources by segmentation ===
    msg('Determining threshold for source detection...',
        msgtype='info')
    # determine threshold for source detection
    # in current implementation, if all inputs are present, the formula is
    # threshold = background + (background_error * snr)
    threshold = detect_threshold(data,
                                 background = bkg_median,
                                 error = data_error+bkg_std,
                                 snr = source_snr)
    # calculate total error including poisson statistics
    try:
        # this is for v0.3 and above
        msg('Calculating total errors including background and Poisson...',
            msgtype='info')
        err_tot = calc_total_error(data, 
                                   bkg_error= data_error+bkg_std, 
                                   effective_gain=0.37)
        gain = None
    # in version earlier than 0.3, this function is not available
    except:
        # error must be of the same shape as the data array
        # this is for v0.2.2
        err_tot = data_error + bkg_std
        gain = 0.37
    msg('Preparing 2D Gaussian kernal...',
        msgtype='info')
    sigma_kernel = fwhm_kernel * gaussian_fwhm_to_sigma
    kernel = Gaussian2DKernel(sigma_kernel,
                           x_size = x_size_kernel,
                           y_size = y_size_kernel)
    # normalise kernel
    # The kernel models are normalized per default, ∫∞−∞f(x)dx=1∫−∞∞f(x)dx=1.
    # But because of the limited kernel array size, the normalization
    # for kernels with an infinite response can differ from one.
    kernel.normalize()
    # obtain a  SegmentationImage object with the same shape as the data,
    # where sources are labeled by different positive integer values.
    # A value of zero is always reserved for the background.
    # if the threshold includes the background level as above, then the image
    # input into detect_sources() should not be background subtracted.
    msg('Segmentation processing...',
        msgtype='info')
    segm = detect_sources(data, threshold, npixels=5, filter_kernel=kernel)
    msg('Segmentation labels are: ' + repr(segm.labels),
        msgtype='info')

    # === Measure regional source properties ===

    # source_properties() assumes that the data have been background-subtracted.
    # Background is the background level that was previously present
    # in the input data.
    # The input background does not get subtracted from the input data,
    # which should already be background-subtracted.
    msg('Extracting source properties...',
        msgtype='info')
    if gain is None:
        # gain is no longer supported in v0.3 and included in total error array
        props_list = source_properties(data-bkg_median, segm, 
                               background = bkg_median,
                               error = err_tot)
    else: # still in v0.2.2
        props_list = source_properties(data-bkg_median, segm, 
                                       background = bkg_median,
                                       error = err_tot,
                                       effective_gain = gain)
    # add more properties that are not automatically calculated
    for i in range(len(props_list)):
        # source_sum is by definition background-subtracted already
        props_list[i].flux = props_list[i].source_sum/exptime
        props_list[i].flux_err = props_list[i].source_sum_err/exptime
        # flux = source_sum / exptime
        # instrumental magnitude = -2.5 * log10(flux)
        props_list[i].mag_instr = -2.5 * np.log10(props_list[i].flux)
        props_list[i].mag_instr_err = -2.5 / props_list[i].flux / np.log(10) \
                                        * props_list[i].flux_err
        # assuming fwhm of a circule gaussian of the same cross section area
        props_list[i].fwhm = gaussian_sigma_to_fwhm * np.sqrt(
                            props_list[i].semimajor_axis_sigma.value
                            * props_list[i].semiminor_axis_sigma.value)
    # make plots and save to images
    # define approximate isophotal ellipses for each object
    apertures = []
    r = 5 # approximate isophotal extent
    for props in props_list:
        position = (props.xcentroid.value, props.ycentroid.value)
        a = props.semimajor_axis_sigma.value * r
        b = props.semiminor_axis_sigma.value * r
        theta = props.orientation.value
        apertures.append(EllipticalAperture(position, a, b, theta=theta))
        
    # === plot and save ===
        
    # if filename ends with fits, remove it in the  filename
    if filename[-5:] == '.fits':
        path_save_prefix = os.path.join(dir_save, filename[0:-5])
    else:
        path_save_prefix = os.path.join(dir_save, filename)
        
    norm_log = mpl_normalize.ImageNormalize(vmin=0, vmax=2000,
                                            stretch = LogStretch())

    if len(props_list) > 0:
        
        # Save segm, porps to object files, and also save props to table file.
        msg('Saving segmentation and source properties to {}...'
            .format(dir_save),
            msgtype='info')
        
        # at least one source was detected
        # create a table of properties
        props_table = properties_table(props_list)
        # add custom columns to the table: mag_instru and flux
        props_table['flux'] = [props_list[i].flux
                                for i in range(len(props_list))]
        props_table['flux_err'] = [props_list[i].flux_err
                                for i in range(len(props_list))]
        props_table['mag_instr'] = [props_list[i].mag_instr
                                        for i in range(len(props_list))]
        props_table['mag_instr_err'] = [props_list[i].mag_instr_err
                                        for i in range(len(props_list))]
        props_table['fwhm'] = [props_list[i].fwhm
                                for i in range(len(props_list))]

        # plot centroid and segmentation outline
        [fig1, ax1] = plt.subplots(figsize=(4, 3))
        ax1.imshow(data, origin='lower', cmap=plt.cm.gray, norm=norm_log)
        ax1.plot(props_table['xcentroid'], props_table['ycentroid'],
                 linestyle='none', color='red',
                 marker='+', markersize=2, markeredgewidth=0.1, alpha=1)
        segm_outline = np.array(segm.outline_segments(), dtype=float)
        segm_outline[segm_outline<1] = np.nan
        # get a copy of the gray color map
        segm_outline_cmap = plt.cm.winter
        # set how the colormap handles 'bad' values
        segm_outline_cmap.set_bad(alpha=0)
        ax1.imshow(segm_outline, 
                   origin='lower', cmap=segm_outline_cmap, alpha=1)
        ax1.get_xaxis().set_visible(False)
        ax1.get_yaxis().set_visible(False)
        fig1.tight_layout()
    
        # segmentation image and aperture using approximate elliptical apertures
        [fig2, ax2] = plt.subplots(figsize=(4, 3))
        rand_cmap = random_cmap(segm.max + 1, random_state=8)
        ax2.imshow(segm, origin='lower', cmap=rand_cmap)
        ax2.plot(props_table['xcentroid'], props_table['ycentroid'],
                 linestyle='none', color='red',
                 marker='+', markersize=2, markeredgewidth=0.1, alpha=1)
        for aperture in apertures:
            aperture.plot(ax=ax2, lw=0.1, alpha=1, color='lime')
        ax2.axis('off')
        ax2.get_xaxis().set_visible(False)
        ax2.get_yaxis().set_visible(False)
        fig2.tight_layout()
        
        try:
            # Enhanced CSV allows preserving table meta-data such as
            # column data types and units.
            # In this way a data table can be stored and read back as ASCII
            # with no loss of information.
            ascii.write(props_table, path_save_prefix + '-props.ecsv',
                        format = 'ecsv')
            # csv for readability in MS excel
            ascii.write(props_table, path_save_prefix + '-props.csv',
                        format = 'csv')
    
            # save figures
            fig1.savefig(path_save_prefix + '-centroid_outline.png',
                         bbox_inches='tight', pad_inches=0, dpi=1200)
            fig2.savefig(path_save_prefix + '-segmentation.png',
                         bbox_inches='tight', pad_inches=0, dpi=2000)
                         
            pp1 = PdfPages(path_save_prefix + '-centroid_outline.pdf')
            pp1.savefig(fig1, dpi=1200)
            pp1.close()
    
            pp2 = PdfPages(path_save_prefix + '-segmentation.pdf')
            pp2.savefig(fig2, dpi=2000)
            pp2.close()

            if dump_pickle:
                # dump segmentation and properties to objects in binary mode
                file_segm = open(path_save_prefix + '-segm.obj', 'wb')
                pickle.dump(segm, file_segm)
                file_props = open(path_save_prefix + '-props.obj', 'wb')
                pickle.dump(props_list, file_props)
    
            msg('Segmentation, properties objects, tables, and images '
                + 'saved to {}'.format(dir_save),
                msgtype='info')
                
        except:
            msg('Unable to write to disk, check permissions.',
                msgtype='error')
            
        # memory leak?
        try:
            plt.close('all')
            del (hdu, hdu_error, data, data_error, header, mask, mask_dilated, 
                 err_tot, kernel, apertures, norm_log, props_table, 
                 segm_outline, segm_outline_cmap, rand_cmap, 
                 fig1, ax1, fig2, ax2, pp1, pp2, file_segm, file_props)
        except:
            pass
        return [segm, props_list]

    else:
        msg('No source detected in {}'.format(path_image_abs),
            msgtype='warning')
            
        # save log scale stretched image, if no source was detected
        [fig0, ax0] = plt.subplots(figsize=(4, 3))
        ax0.imshow(data, origin='lower', cmap=plt.cm.gray, norm=norm_log)
        ax0.get_xaxis().set_visible(False)
        ax0.get_yaxis().set_visible(False)
        
        try:
            fig0.savefig(path_save_prefix + '-logstretch.png',
                         bbox_inches='tight', pad_inches=0, dpi=1200)
            pp0 = PdfPages(path_save_prefix + '-logstretch.pdf')
            pp0.savefig(fig0, dpi=1200)
            pp0.close()
            
        except:
            msg('Unable to write to disk, check permissions.',
                msgtype='error')
            
        return [None, []]