Example #1
0
def test_apertures_all():
    """Test that aperture subpixel sampling works"""

    data = np.random.rand(*data_shape)
    r = 3.
    rtol = 1.e-8

    for subpix in [0, 1, 5]:
        flux_ref, fluxerr_ref, flag_ref = sep.sum_circle(data,
                                                         x,
                                                         y,
                                                         r,
                                                         subpix=subpix)

        flux, fluxerr, flag = sep.sum_circann(data, x, y, 0., r, subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)

        flux, fluxerr, flag = sep.sum_ellipse(data,
                                              x,
                                              y,
                                              r,
                                              r,
                                              0.,
                                              subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)

        flux, fluxerr, flag = sep.sum_ellipse(data,
                                              x,
                                              y,
                                              1.,
                                              1.,
                                              0.,
                                              r=r,
                                              subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)
Example #2
0
def sep_phot(exp_data, asteroid_id, ap=10.0):
    """
    Measure background of postage stamp and the flux_err of the asteroid
    """

    data2 = np.ones(exp_data.shape) * exp_data
    # np.copyto(data2, exp_data)
    try:
        bkg = sep.Background(data2)
    except ValueError:
        data3 = data2.byteswap(True).newbyteorder()
        bkg = sep.Background(data3)

    # Directly subtract the background from the data in place
    bkg.subfrom(data2)

    # calculate the Kron radius for each object, then we perform elliptical aperture photometry within that radius
    kronrad, krflag = sep.kron_radius(data2, asteroid_id[_XMID_HEADER], asteroid_id[_YMID_HEADER],
                                      asteroid_id[_A_HEADER],
                                      asteroid_id['b'], asteroid_id[_THETA_HEADER], ap)
    flux, fluxerr, flag = sep.sum_ellipse(data2, asteroid_id[_XMID_HEADER], asteroid_id[_YMID_HEADER],
                                          asteroid_id[_A_HEADER], asteroid_id[_B_HEADER],
                                          asteroid_id[_THETA_HEADER], 2.5 * kronrad, subpix=1, err=bkg.globalrms)

    return bkg.globalback, flux, fluxerr
Example #3
0
def sep_phot(data, ap, th):
    """
    Preforms photometry by SEP, similar to source extractor
    """

    # Measure a spatially variable background of some image data (np array)
    try:
        bkg = sep.Background(data)  # , mask=mask, bw=64, bh=64, fw=3, fh=3) # optional parameters
    except ValueError:
        data = data.byteswap(True).newbyteorder()
        bkg = sep.Background(data)  # , mask=mask, bw=64, bh=64, fw=3, fh=3) # optional parameters

    # Directly subtract the background from the data in place
    bkg.subfrom(data)

    # for the background subtracted data, detect objects in data given some threshold
    thresh = th * bkg.globalrms  # ensure the threshold is high enough wrt background
    objs = sep.extract(data, thresh)
    # calculate the Kron radius for each object, then we perform elliptical aperture photometry within that radius
    kronrad, krflag = sep.kron_radius(data, objs['x'], objs['y'], objs['a'], objs['b'], objs['theta'], ap)
    flux, fluxerr, flag = sep.sum_ellipse(data, objs['x'], objs['y'], objs['a'], objs['b'], objs['theta'],
                                          2.5 * kronrad, subpix=1)
    flag |= krflag  # combine flags into 'flag'

    r_min = 1.75  # minimum diameter = 3.5
    use_circle = kronrad * np.sqrt(objs['a'] * objs['b']) < r_min
    x = objs['x']
    y = objs['y']
    cflux, cfluxerr, cflag = sep.sum_circle(data, x[use_circle], y[use_circle],
                                            r_min, subpix=1)
    flux[use_circle] = cflux
    fluxerr[use_circle] = cfluxerr
    flag[use_circle] = cflag

    return objs
Example #4
0
File: test.py Project: cmccully/sep
def test_apertures_exact():
    """Test area as measured by exact aperture modes on array of ones"""

    theta = np.random.uniform(-np.pi/2., np.pi/2., naper)
    ratio = np.random.uniform(0.2, 1.0, naper)
    r = 3.

    for dt in SUPPORTED_IMAGE_DTYPES:
        data = np.ones(data_shape, dtype=dt)
        for r in [0.5, 1., 3.]:
            flux, fluxerr, flag = sep.sum_circle(data, x, y, r, subpix=0)
            assert_allclose(flux, np.pi*r**2)

            rout = r*1.1
            flux, fluxerr, flag = sep.sum_circann(data, x, y, r, rout,
                                                  subpix=0)
            assert_allclose(flux, np.pi*(rout**2 - r**2))

            flux, fluxerr, flag = sep.sum_ellipse(data, x, y, 1., ratio,
                                                  theta, r=r, subpix=0)
            assert_allclose(flux, np.pi*ratio*r**2)

            rout = r*1.1
            flux, fluxerr, flag = sep.sum_ellipann(data, x, y, 1., ratio,
                                                   theta, r, rout, subpix=0)
            assert_allclose(flux, np.pi*ratio*(rout**2 - r**2))
Example #5
0
    def AperaturePhoto(self,filter,objs):
        print 'Running Aperature Photometry on %s......'%filter
        kronrad, krflag = sep.kron_radius(self.dataList[filter],
                                          objs['x'], objs['y'],
                                          objs['a'], objs['b'],
                                          objs['theta'],
                                          6.0)

        flux, fluxerr, flag = sep.sum_ellipse(self.dataList[filter],
                                              objs['x'], objs['y'],
                                              objs['a'], objs['b'],
                                              objs['theta'],
                                              2.5*kronrad, subpix=1,
                                              err=self.bkgRMS[filter])
        #use circular aperature photometry if the kronradius is too small. see http://sep.readthedocs.org/en/v0.2.x/apertures.html
        r_min = 1.75 # minimum diameter = 3.5
        use_circle = kronrad * np.sqrt(objs['a']*objs['b']) < r_min
        cflux, cfluxerr, cflag = sep.sum_circle(self.dataList[filter],
                                                objs['x'][use_circle],
                                                objs['y'][use_circle],
                                                r_min, subpix=1,err=self.bkgRMS[filter])
        flux[use_circle] = cflux
        fluxerr[use_circle] = cfluxerr
        flag[use_circle] = cflag
        #convert flux to magnitudes using the appropriate zeropoint
        #absolute flux measurement (AB for Z-PEG)
        mag = -2.5*np.log10(flux)+self.zeroPoints[filter]
        #calculate magerr
        fluxdown = flux - fluxerr
        fluxup = flux + fluxerr
        magup = -2.5*np.log10(fluxdown) + self.zeroPoints[filter]
        magdown = -2.5*np.log10(fluxup) + self.zeroPoints[filter]
        magerr = ((magup - mag) + (mag-magdown))/2.

        return mag, magerr
Example #6
0
def calc_flux_err(a: float, img_back: ndarray, x: float, y: float,
                  elongation: float, theta: float, err: ndarray, mask: ndarray,
                  gain: float) -> float:
    """
    Calculate flux error for a source depending on the aperture size; used
    by adaptive photometry to calculate the optimal aperture

    :param a: aperture size
    :param img_back: image with background subtracted
    :param x: source centroid X
    :param y: source centroid Y
    :param elongation: isophotal a/b ratio for the source
    :param theta: major axis position angle in radians
    :param err: background RMS
    :param mask: image mask
    :param gain: inverse gain in e-/ADU

    :return: flux error for the given aperture
    """
    flux, flux_err, flags = sep.sum_ellipse(
        img_back, [x], [y], a, a/elongation, theta, 1, err=err, mask=mask,
        gain=gain, subpix=0)
    if flags:
        raise ValueError('flags = {}'.format(flags))
    if flux <= 0:
        raise ValueError('flux = {}'.format(flux))
    return flux_err/flux
Example #7
0
File: test.py Project: cmccully/sep
def test_apertures_small_ellipse_exact():
    """Regression test for a bug that manifested primarily when x == y."""

    data = np.ones(data_shape)
    r = 0.3
    rtol=1.e-10
    flux, fluxerr, flag = sep.sum_ellipse(data, x, x, r, r, 0., subpix=0)
    assert_allclose(flux, np.pi*r**2, rtol=rtol)
Example #8
0
def catalog(ccd, bgf, catf, db, config, logger):
    bg = fits.open(bgf)
    im = ccd.data - bg['background'].data
    ps = ccd.meta['SCALE'] * ccd.meta.get('REBIN', 1)

    bgrms = bg['background'].header['bgrms']
    objects = sep.extract(im, 2, err=bgrms, mask=ccd.mask)
    logger.info('Found {} sources.'.format(len(objects)))

    rap = max(ccd.meta['SEEING'] * 2, 5 / ps)
    flux, fluxerr, flag = sep.sum_circle(im,
                                         objects['x'],
                                         objects['y'],
                                         rap,
                                         err=bgrms)

    # avoid theta rounding error
    theta = np.maximum(np.minimum(objects['theta'], np.pi / 2.00001),
                       -np.pi / 2.00001)
    kronrad, krflag = sep.kron_radius(im, objects['x'], objects['y'],
                                      objects['a'], objects['b'], theta, 6.0)
    krflux, krfluxerr, _flag = sep.sum_ellipse(im,
                                               objects['x'],
                                               objects['y'],
                                               objects['a'],
                                               objects['b'],
                                               theta,
                                               2.5 * kronrad,
                                               subpix=1,
                                               err=bgrms)
    krflag |= _flag

    # an additional background estimate, which should help when there are
    # large extended sources in scene: IN TESTS, THIS DID NOT AFFECT RESULTS
    # for i in range(len(objects)):
    #    krflux[i], krfluxerr[i] = bg_subtract2(im, objects[i], krflux[i],
    #                                           krfluxerr[i])
    #    flux[i], fluxerr[i] = bg_subtract2(im, objects[i], flux[i],
    #                                       fluxerr[i], r=rap)

    if ccd.wcs.wcs.crval[0] == ccd.wcs.wcs.crval[1]:
        ra, dec = np.zeros((2, len(objects)))
    else:
        ra, dec = ccd.wcs.all_pix2world(objects['x'], objects['y'], 0)

    tab = Table(
        (objects['x'], objects['y'], ra, dec, flux, fluxerr, flag,
         objects['a'], objects['b'], theta, kronrad, krflux, krfluxerr,
         krflag),
        names=('x', 'y', 'ra', 'dec', 'flux', 'fluxerr', 'flag', 'a', 'b',
               'theta', 'kronrad', 'krflux', 'krfluxerr', 'krflag'))

    hdu = fits.HDUList()
    hdu.append(fits.BinTableHDU(tab, name='cat'))
    hdu['cat'].header['RADIUS'] = (rap * ps,
                                   'aperture photometry radius, arcsec')
    hdu.writeto(catf, overwrite=True)
Example #9
0
def plot_light_curves(diff_cube, unique_extracted_objects):
    # The diff_cube has to be byte swapped BEFORE being sent as parameter (diff_cube.byteswap(True).newbyteorder()), otherwise the method is not goint to work. Unique_extracted_objects only work for elliptic-shapped apertures

    # We get the number of frames from the cube
    frame_data = [i for i in range(len(diff_cube))]
    # Random colours array
    colours = [
        (random.uniform(0.5, 1), random.uniform(0.5, 1), random.uniform(0.5, 1))
        for i in range(len(unique_extracted_objects))
    ]

    maxVal = 0
    minVal = float("inf")

    plt.figure(2, figsize=(10, 12))

    # Bonus: Show the image with the sources on the same colour than the plots.
    if len(diff_cube) == 1:
        plt.imshow(diff_cube[0], cmap="gray", vmin=1, vmax=12)
    else:
        plt.imshow(diff_cube[1], cmap="gray", vmin=1, vmax=12)
    plt.colorbar()
    for i, extracted_obj in enumerate(unique_extracted_objects):
        positions = (extracted_obj[0], extracted_obj[1])
        apertures = CircularAperture(positions, r=5.0)
        apertures.plot(color=colours[i], linewidth=10.0, lw=2.5, alpha=0.5)
    # For every object we are going to calculate the aperture
    plt.figure(1, figsize=(20, 12))
    for i, extracted_obj in enumerate(unique_extracted_objects):
        ap_data = []
        # The standard size of each independent figure
        # plt.figure(i, figsize=(10, 12))
        # For every frame...
        for frame in diff_cube:
            diff_cube_test = frame.copy()
            # The parameters passed in order are x, y, a, b and theta
            flux, fluxerr, flag = sep.sum_ellipse(
                diff_cube_test,
                x=extracted_obj[0],
                y=extracted_obj[1],
                a=extracted_obj[2],
                b=extracted_obj[3],
                theta=extracted_obj[4],
            )

            ap_data.append(flux)
        maxVal = np.maximum(maxVal, np.max(ap_data))
        minVal = np.minimum(minVal, np.min(ap_data))
        # Hard-coded value!!! ALERT!!!

        # Plot every curve as a dotted line with the points visible
        plt.plot(frame_data, ap_data, "-o", color=colours[i], linewidth=5.0)
    plt.ylim((minVal * 1.1, maxVal * 0.9))
    # Voila
    plt.show()
Example #10
0
    def _measure(self, img, sources, mask=None):

        logger.info('measuring source parameters')

        # HACK: issues with numerical precision
        # must have pi/2 <= theta <= npi/2
        sources[np.abs(np.abs(sources['theta']) - np.pi/2) < 1e-6] = np.pi/2

        for p in ['x', 'y', 'a', 'b', 'theta']:
            sources = sources[~np.isnan(sources[p])]

        # calculate "AUTO" parameters
        kronrad, krflag = sep.kron_radius(
            img, sources['x'], sources['y'], sources['a'], sources['b'],
            sources['theta'], 6.0, mask=mask)
    
        flux, fluxerr, flag = sep.sum_ellipse(
            img, sources['x'], sources['y'], sources['a'], sources['b'],
            sources['theta'], 2.5*kronrad, subpix=5, mask=mask)
        flag |= krflag  # combine flags into 'flag'

        sources = sources[~np.isnan(flux)]
        flux = flux[~np.isnan(flux)]
        sources = sources[flux > 0]
        flux = flux[flux > 0]

        mag_auto = utils.zpt - 2.5*np.log10(flux)
        r, flag = sep.flux_radius(
            img, sources['x'], sources['y'], 6.*sources['a'], 0.5,
            normflux=flux, subpix=5, mask=mask)

        sources['mag_auto'] = mag_auto
        sources['flux_auto'] = flux
        sources['flux_radius'] = r * utils.pixscale

        # approximate fwhm 
        r_squared = sources['a']**2 + sources['b']**2
        sources['fwhm'] = 2 * np.sqrt(np.log(2) * r_squared) *  utils.pixscale
        
        q = sources['b'] / sources['a']
        area = np.pi * q * sources['flux_radius']**2
        sources['mu_ave_auto'] = sources['mag_auto']  + 2.5 * np.log10(2*area)
        
        area_arcsec = np.pi * (self.psf_fwhm/2)**2 * utils.pixscale**2
        flux, fluxerr, flag = sep.sum_circle(
            img, sources['x'], sources['y'], self.psf_fwhm/2, 
            subpix=5, mask=mask)
        flux[flux<=0] = np.nan
        mu_0 = utils.zpt - 2.5*np.log10(flux / area_arcsec)

        sources['mu_0_aper'] = mu_0

        return sources
Example #11
0
File: test.py Project: cmccully/sep
def test_apertures_all():
    """Test that aperture subpixel sampling works"""

    data = np.random.rand(*data_shape)
    r = 3.
    rtol=1.e-8

    for subpix in [0, 1, 5]:
        flux_ref, fluxerr_ref, flag_ref = sep.sum_circle(data, x, y, r,
                                                         subpix=subpix)

        flux, fluxerr, flag = sep.sum_circann(data, x, y, 0., r,
                                              subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)

        flux, fluxerr, flag = sep.sum_ellipse(data, x, y, r, r, 0.,
                                              subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)

        flux, fluxerr, flag = sep.sum_ellipse(data, x, y, 1., 1., 0., r=r,
                                              subpix=subpix)
        assert_allclose(flux, flux_ref, rtol=rtol)
Example #12
0
File: test.py Project: cmccully/sep
def test_aperture_bkgann_overlapping():
    """Test bkgann functionality in circular & elliptical apertures."""

    # If bkgann overlaps aperture exactly, result should be zero
    # (with subpix=1)
    data = np.random.rand(*data_shape)
    r = 5.
    f, _, _ = sep.sum_circle(data, x, y, r, bkgann=(0., r), subpix=1)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)

    f, _, _ = sep.sum_ellipse(data, x, y, 2., 1., np.pi/4., r=r,
                              bkgann=(0., r), subpix=1)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)
Example #13
0
File: test.py Project: fred3m/sep
def test_aperture_bkgann_ones():
    """Test bkgann functionality with flat data"""

    data = np.ones(data_shape)
    r=5.
    bkgann=(6., 8.)

    # On flat data, result should be zero for any bkgann and subpix
    f, _, _ = sep.sum_circle(data, x, y, r, bkgann=bkgann)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)
   
    f, _, _ = sep.sum_ellipse(data, x, y, 2., 1., np.pi/4., r, bkgann=bkgann)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)
Example #14
0
def find_flux(tbdat_sub, objects, kronrad, kronflag):
	flux, fluxerr, flag = sep.sum_ellipse(tbdat_sub, objects['x'], objects['y'], objects['a'], objects['b'], objects['theta'], pho_auto_A = (2.5*kronrad), err = bkg.globalrms, subpix=1)
	flag |=kronflag #combines all flags
	r_min = 1.75 #minimum diameter = 3.5
	use_circle = kronrad * np.sqrt(a * b) < r_min
	cflux, cfluxerr, cflag = sep.sum_circle(tbdat_sub, objects['x'][use_circle], objects['y'][use_circle], r_min, subpix=1)
	flux[use_circle] = cflux
	fluxerr[use_circle] = cfluxerr
	flag[use_circle] = cflag
	r, rflag = sep.flux_radius(data, x, y, 6.0*objects['a'], rmax = 0.5, normflux = flux, subpix =5)
	sig = 2.0 / (2.35*r) # r from sep.flux_radius() above, with fluxfrac = 0.5
	xwin, ywin, wflag = sep.winpos(tbdat_sub, objects['x'], objects['y'], sig)
	return flux, fluxerr, flag, r, xwin, ywin
Example #15
0
def sextractor(im,err=None,mask=None,nsig=5.0,gain=1.0):

    # Check byte order, SEP needs little endian
    if im.dtype.byteorder == '>':
        data = im.byteswap().newbyteorder()
    else:
        data = im

    # Background estimation and subtraction
    bkg = sep.Background(data, mask, bw=256, bh=256, fw=3, fh=3)
    bkg_image = bkg.back()
    data_sub = data-bkg
    #data_sub[data>50000]=0.0
    # Detect and extract objects
    if err is None:
        objects = sep.extract(data_sub, nsig, err=bkg.globalrms, mask=mask)
    else:
        objects = sep.extract(data_sub, nsig, err=err, mask=mask)
        
    # Get mag_auto in 2 steps
    kronrad, krflag = sep.kron_radius(data_sub, objects['x'], objects['y'], objects['a'], objects['b'],
                                      objects['theta'], 6.0, mask=mask)
    flux, fluxerr, flag = sep.sum_ellipse(data_sub, objects['x'], objects['y'], objects['a'], objects['b'],
                                          objects['theta'], 2.5*kronrad, subpix=1, err=err, mask=mask, gain=gain)
    flag |= krflag  # combine flags into 'flag'

    # Use circular aperture if Kron radius is too small
    r_min = 1.75  # minimum diameter = 3.5
    use_circle = kronrad * np.sqrt(objects['a'] * objects['b']) < r_min
    if np.sum(use_circle)>0:
        cflux, cfluxerr, cflag = sep.sum_circle(data_sub, objects['x'][use_circle], objects['y'][use_circle],
                                                r_min, subpix=1, err=err, mask=mask, gain=gain)
        flux[use_circle] = cflux
        fluxerr[use_circle] = cfluxerr
        flag[use_circle] = cflag
    mag_auto = -2.5*np.log10(flux)+25.0
    magerr_auto = 1.0857*fluxerr/flux
    
    # Make the final catalog
    newdt = np.dtype([('kronrad',float),('flux_auto',float),('fluxerr_auto',float),('mag_auto',float),('magerr_auto',float)])
    cat = dln.addcatcols(objects,newdt)
    cat['flag'] |= flag
    cat['kronrad'] = kronrad
    cat['flux_auto'] = flux
    cat['fluxerr_auto'] = fluxerr
    cat['mag_auto'] = mag_auto
    cat['magerr_auto'] = magerr_auto
    
    return cat
Example #16
0
    def _get_flux_auto(self, objs):
        flux_auto = np.zeros(objs.size) - 9999.0
        fluxerr_auto = np.zeros(objs.size) - 9999.0
        flux_radius = np.zeros(objs.size) - 9999.0
        kron_radius = np.zeros(objs.size) - 9999.0

        w, = np.where((objs['a'] >= 0.0) & (objs['b'] >= 0.0)
                      & (objs['theta'] >= -np.pi / 2.)
                      & (objs['theta'] <= np.pi / 2.))

        if w.size > 0:
            kron_radius[w], krflag = sep.kron_radius(
                self.image,
                objs['x'][w],
                objs['y'][w],
                objs['a'][w],
                objs['b'][w],
                objs['theta'][w],
                6.0,
            )
            objs['flag'][w] |= krflag

            aper_rad = 2.5 * kron_radius
            flux_auto[w], fluxerr_auto[w], flag_auto = \
                sep.sum_ellipse(
                    self.image,
                    objs['x'][w],
                    objs['y'][w],
                    objs['a'][w],
                    objs['b'][w],
                    objs['theta'][w],
                    aper_rad[w],
                    subpix=1,
                )
            objs['flag'][w] |= flag_auto

            flux_radius[w], frflag = sep.flux_radius(
                self.image,
                objs['x'][w],
                objs['y'][w],
                6. * objs['a'][w],
                PHOT_FLUXFRAC,
                normflux=flux_auto[w],
                subpix=5,
            )
            objs['flag'][w] |= frflag  # combine flags into 'flag'

        return flux_auto, fluxerr_auto, flux_radius, kron_radius
Example #17
0
def make_sep_catalog(data,
                     header,
                     options,
                     mask=None,
                     min_sep=10.,
                     do_bgsub=False):

    try:
        bkg = sep.Background(data, mask, bw=32, bh=32, fw=3, fh=3)
    except ValueError:
        data = data.byteswap().newbyteorder()
        bkg = sep.Background(data, mask, bw=32, bh=32, fw=3, fh=3)

    if do_bgsub:
        error = np.sqrt(data)
        data_bgsub = data - bkg
    else:
        error = bkg.globalrms
        data_bgsub = data
    sources = sep.extract(data_bgsub, err=error, mask=mask, **options['sep'])

    dists = ((sources['x'] - sources['x'][:, np.newaxis])**2 +
             (sources['y'] - sources['y'][:, np.newaxis])**2)**0.5
    closest = np.partition(dists, 1)[:, 1]
    sources = sources[closest > min_sep]

    t = table.Table(sources)
    kronrad, krflag = sep.kron_radius(data_bgsub, sources['x'], sources['y'],
                                      sources['a'], sources['b'],
                                      sources['theta'], 6.0)

    flux, fluxerr, flag = sep.sum_ellipse(data_bgsub,
                                          sources['x'],
                                          sources['y'],
                                          sources['a'],
                                          sources['b'],
                                          np.pi / 2.0,
                                          2.5 * kronrad,
                                          subpix=1,
                                          err=error)

    t['mag'] = -2.5 * np.log10(flux)
    t['magerr'] = np.log(10) / 2.5 * fluxerr / flux
    t['ra'], t['dec'] = WCS(header).all_pix2world(t['x'], t['y'], 0)

    t = t['x', 'y', 'mag', 'magerr', 'ra', 'dec']

    return t
Example #18
0
def extract(data):
	bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3)
	bkg.subfrom(data)
	objs = sep.extract(data, 1.5*bkg.globalrms)
	flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 5.,
                                         err=bkg.globalrms)
	kr, flag = sep.kron_radius(data, objs['x'], objs['y'], objs['a'],
                               			objs['b'], objs['theta'], 6.0)
	eflux, efluxerr, eflag = sep.sum_ellipse(data, objs['x'], objs['y'],
                                          objs['a'], objs['b'],
                                          objs['theta'], r=2.5 * kr,
                                          err=bkg.globalrms, subpix=1)
	retstr = ""
	for i in range(len(objs['x'])):
		retstr = retstr+(str(objs['x'][i])+"\t"+str(objs['y'][i])+"\t"+str(flux[i])+"\t"+str(fluxerr[i])+"\t"+str(kr[i])+"\t"+str(eflux[i])+"\t"+str(efluxerr[i])+"\t"+str(flag[i])+"\n")
	return retstr
Example #19
0
File: test.py Project: cmccully/sep
def test_aperture_bkgann_ones():
    """Test bkgann functionality with flat data"""

    data = np.ones(data_shape)
    r=5.
    bkgann=(6., 8.)

    # On flat data, result should be zero for any bkgann and subpix
    f, fe, _ = sep.sum_circle(data, x, y, r, bkgann=bkgann, gain=1.)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)

    # for all ones data and no error array, error should be close to
    # sqrt(Npix_aper + Npix_ann * (Npix_aper**2 / Npix_ann**2))
    aper_area = np.pi * r**2
    bkg_area = np.pi * (bkgann[1]**2 - bkgann[0]**2)
    expected_error = np.sqrt(aper_area + bkg_area * (aper_area/bkg_area)**2)
    assert_allclose(fe, expected_error, rtol=0.1)

    f, _, _ = sep.sum_ellipse(data, x, y, 2., 1., np.pi/4., r, bkgann=bkgann)
    assert_allclose(f, 0., rtol=0., atol=1.e-13)
Example #20
0
def findSpot(data, sigma):
    image=data
    #m, s = np.mean(image), np.std(image)
    bkg = sep.Background(image, bw=32, bh=32, fw=3, fh=3)
    objs = sep.extract(image-bkg, sigma, err=bkg.globalrms)
    aper_radius=3
    
    # Calculate the Kron Radius
    kronrad, krflag = sep.kron_radius(image, objs['x'], objs['y'], \
        objs['a'], objs['b'], objs['theta'], aper_radius)

    r_min = 3
    use_circle = kronrad * np.sqrt(objs['a'] * objs['b'])
    cinx=np.where(use_circle <= r_min)
    einx=np.where(use_circle > r_min)

    # Calculate the equivalent of FLUX_AUTO
    flux, fluxerr, flag = sep.sum_ellipse(image, objs['x'][einx], objs['y'][einx], \
        objs['a'][einx], objs['b'][einx], objs['theta'][einx], 2.5*kronrad[einx],subpix=1)		

    cflux, cfluxerr, cflag = sep.sum_circle(image, objs['x'][cinx], objs['y'][cinx],
                                    objs['a'][cinx], subpix=1)

    # Adding half pixel to measured coordinate.  
    objs['x'] =  objs['x']+0.5
    objs['y'] =  objs['y']+0.5

    objs['flux'][einx]=flux
    objs['flux'][cinx]=cflux


    r, flag = sep.flux_radius(image, objs['x'], objs['y'], \
        6*objs['a'], 0.3,normflux=objs['flux'], subpix=5)

    flag |= krflag
 
    objs=rfn.append_fields(objs, 'r', data=r, usemask=False)

    objects=objs[:]
    
    return objects
Example #21
0
def aperture_photometry(data, x, y, r, r_in, r_out,
                        elipse = False, abtheta = None,
                        rms = None,
                        *args, **kwargs):
    '''
    Do the aperture photometry with local sky subtraction.

    Parameters:
        data : ~numpy.ndarray~
            2D image data.
        x, y : list of list of float
            The list containing the pairs (x,y) of the objects.
        r : float
            The radius of the circular aperture to do the sum.
        r_in : float
            The internal radius of the sky annulus.
        r_out : float
            The external radius of the sky annulus.
        elipse : bool
            Tell the program if you want to do the photometry with eliptical
            aperture. If True, you have to pass the 'abtheta' argument, giving
            the elipse properties.
        **The kwargs will be passed integrally to the sep functions.

    Returns:
        flux, fluxerr, flags : ~numpy.ndarray~
            The sum of the aperture, with annulus sky subtraction, and its error.
    '''
    data = _fix_data(data)

    if elipse:
        if abtheta is None:
            raise ValueError("You must give the 'abtheta' argument if you want elipse photometry.")
        a, b, theta = _extract_abtheta(abtheta)
        return sep.sum_ellipse(data, x, y, a, b, theta, r, err=rms, bkgann=(r_in, r_out), **kwargs)
    else:
        return sep.sum_circle(data, x, y, r, err=rms, bkgann=(r_in, r_out), **kwargs)
Example #22
0
    def do_stage(self, image):
        try:
            # Set the number of source pixels to be 5% of the total. This keeps us safe from
            # satellites and airplanes.
            sep.set_extract_pixstack(int(image.nx * image.ny * 0.05))

            data = image.data.copy()
            error = (np.abs(data) + image.readnoise**2.0)**0.5
            mask = image.bpm > 0

            # Fits can be backwards byte order, so fix that if need be and subtract
            # the background
            try:
                bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
            except ValueError:
                data = data.byteswap(True).newbyteorder()
                bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
            bkg.subfrom(data)

            # Do an initial source detection
            # TODO: Add back in masking after we are sure SEP works
            sources = sep.extract(data,
                                  self.threshold,
                                  minarea=self.min_area,
                                  err=error,
                                  deblend_cont=0.005)

            # Convert the detections into a table
            sources = Table(sources)

            # We remove anything with a detection flag >= 8
            # This includes memory overflows and objects that are too close the edge
            sources = sources[sources['flag'] < 8]

            sources = array_utils.prune_nans_from_table(sources)

            # Calculate the ellipticity
            sources['ellipticity'] = 1.0 - (sources['b'] / sources['a'])

            # Fix any value of theta that are invalid due to floating point rounding
            # -pi / 2 < theta < pi / 2
            sources['theta'][sources['theta'] > (np.pi / 2.0)] -= np.pi
            sources['theta'][sources['theta'] < (-np.pi / 2.0)] += np.pi

            # Calculate the kron radius
            kronrad, krflag = sep.kron_radius(data, sources['x'], sources['y'],
                                              sources['a'], sources['b'],
                                              sources['theta'], 6.0)
            sources['flag'] |= krflag
            sources['kronrad'] = kronrad

            # Calcuate the equivilent of flux_auto
            flux, fluxerr, flag = sep.sum_ellipse(data,
                                                  sources['x'],
                                                  sources['y'],
                                                  sources['a'],
                                                  sources['b'],
                                                  np.pi / 2.0,
                                                  2.5 * kronrad,
                                                  subpix=1,
                                                  err=error)
            sources['flux'] = flux
            sources['fluxerr'] = fluxerr
            sources['flag'] |= flag

            # Do circular aperture photometry for diameters of 1" to 6"
            for diameter in [1, 2, 3, 4, 5, 6]:
                flux, fluxerr, flag = sep.sum_circle(data,
                                                     sources['x'],
                                                     sources['y'],
                                                     diameter / 2.0 /
                                                     image.pixel_scale,
                                                     gain=1.0,
                                                     err=error)
                sources['fluxaper{0}'.format(diameter)] = flux
                sources['fluxerr{0}'.format(diameter)] = fluxerr
                sources['flag'] |= flag

            # Calculate the FWHMs of the stars:
            fwhm = 2.0 * (np.log(2) *
                          (sources['a']**2.0 + sources['b']**2.0))**0.5
            sources['fwhm'] = fwhm

            # Cut individual bright pixels. Often cosmic rays
            sources = sources[fwhm > 1.0]

            # Measure the flux profile
            flux_radii, flag = sep.flux_radius(data,
                                               sources['x'],
                                               sources['y'],
                                               6.0 * sources['a'],
                                               [0.25, 0.5, 0.75],
                                               normflux=sources['flux'],
                                               subpix=5)
            sources['flag'] |= flag
            sources['fluxrad25'] = flux_radii[:, 0]
            sources['fluxrad50'] = flux_radii[:, 1]
            sources['fluxrad75'] = flux_radii[:, 2]

            # Calculate the windowed positions
            sig = 2.0 / 2.35 * sources['fluxrad50']
            xwin, ywin, flag = sep.winpos(data, sources['x'], sources['y'],
                                          sig)
            sources['flag'] |= flag
            sources['xwin'] = xwin
            sources['ywin'] = ywin

            # Calculate the average background at each source
            bkgflux, fluxerr, flag = sep.sum_ellipse(bkg.back(),
                                                     sources['x'],
                                                     sources['y'],
                                                     sources['a'],
                                                     sources['b'],
                                                     np.pi / 2.0,
                                                     2.5 * sources['kronrad'],
                                                     subpix=1)
            # masksum, fluxerr, flag = sep.sum_ellipse(mask, sources['x'], sources['y'],
            #                                         sources['a'], sources['b'], np.pi / 2.0,
            #                                         2.5 * kronrad, subpix=1)

            background_area = (
                2.5 * sources['kronrad']
            )**2.0 * sources['a'] * sources['b'] * np.pi  # - masksum
            sources['background'] = bkgflux
            sources['background'][background_area > 0] /= background_area[
                background_area > 0]
            # Update the catalog to match fits convention instead of python array convention
            sources['x'] += 1.0
            sources['y'] += 1.0

            sources['xpeak'] += 1
            sources['ypeak'] += 1

            sources['xwin'] += 1.0
            sources['ywin'] += 1.0

            sources['theta'] = np.degrees(sources['theta'])

            catalog = sources['x', 'y', 'xwin', 'ywin', 'xpeak', 'ypeak',
                              'flux', 'fluxerr', 'peak', 'fluxaper1',
                              'fluxerr1', 'fluxaper2', 'fluxerr2', 'fluxaper3',
                              'fluxerr3', 'fluxaper4', 'fluxerr4', 'fluxaper5',
                              'fluxerr5', 'fluxaper6', 'fluxerr6',
                              'background', 'fwhm', 'a', 'b', 'theta',
                              'kronrad', 'ellipticity', 'fluxrad25',
                              'fluxrad50', 'fluxrad75', 'x2', 'y2', 'xy',
                              'flag']

            # Add the units and description to the catalogs
            catalog['x'].unit = 'pixel'
            catalog['x'].description = 'X coordinate of the object'
            catalog['y'].unit = 'pixel'
            catalog['y'].description = 'Y coordinate of the object'
            catalog['xwin'].unit = 'pixel'
            catalog['xwin'].description = 'Windowed X coordinate of the object'
            catalog['ywin'].unit = 'pixel'
            catalog['ywin'].description = 'Windowed Y coordinate of the object'
            catalog['xpeak'].unit = 'pixel'
            catalog['xpeak'].description = 'X coordinate of the peak'
            catalog['ypeak'].unit = 'pixel'
            catalog['ypeak'].description = 'Windowed Y coordinate of the peak'
            catalog['flux'].unit = 'count'
            catalog[
                'flux'].description = 'Flux within a Kron-like elliptical aperture'
            catalog['fluxerr'].unit = 'count'
            catalog[
                'fluxerr'].description = 'Error on the flux within Kron aperture'
            catalog['peak'].unit = 'count'
            catalog['peak'].description = 'Peak flux (flux at xpeak, ypeak)'
            for diameter in [1, 2, 3, 4, 5, 6]:
                catalog['fluxaper{0}'.format(diameter)].unit = 'count'
                catalog['fluxaper{0}'.format(
                    diameter
                )].description = 'Flux from fixed circular aperture: {0}" diameter'.format(
                    diameter)
                catalog['fluxerr{0}'.format(diameter)].unit = 'count'
                catalog['fluxerr{0}'.format(
                    diameter
                )].description = 'Error on Flux from circular aperture: {0}"'.format(
                    diameter)

            catalog['background'].unit = 'count'
            catalog[
                'background'].description = 'Average background value in the aperture'
            catalog['fwhm'].unit = 'pixel'
            catalog['fwhm'].description = 'FWHM of the object'
            catalog['a'].unit = 'pixel'
            catalog['a'].description = 'Semi-major axis of the object'
            catalog['b'].unit = 'pixel'
            catalog['b'].description = 'Semi-minor axis of the object'
            catalog['theta'].unit = 'degree'
            catalog['theta'].description = 'Position angle of the object'
            catalog['kronrad'].unit = 'pixel'
            catalog['kronrad'].description = 'Kron radius used for extraction'
            catalog['ellipticity'].description = 'Ellipticity'
            catalog['fluxrad25'].unit = 'pixel'
            catalog[
                'fluxrad25'].description = 'Radius containing 25% of the flux'
            catalog['fluxrad50'].unit = 'pixel'
            catalog[
                'fluxrad50'].description = 'Radius containing 50% of the flux'
            catalog['fluxrad75'].unit = 'pixel'
            catalog[
                'fluxrad75'].description = 'Radius containing 75% of the flux'
            catalog['x2'].unit = 'pixel^2'
            catalog[
                'x2'].description = 'Variance on X coordinate of the object'
            catalog['y2'].unit = 'pixel^2'
            catalog[
                'y2'].description = 'Variance on Y coordinate of the object'
            catalog['xy'].unit = 'pixel^2'
            catalog['xy'].description = 'XY covariance of the object'
            catalog[
                'flag'].description = 'Bit mask of extraction/photometry flags'

            catalog.sort('flux')
            catalog.reverse()

            # Save some background statistics in the header
            mean_background = stats.sigma_clipped_mean(bkg.back(), 5.0)
            image.header['L1MEAN'] = (
                mean_background,
                '[counts] Sigma clipped mean of frame background')

            median_background = np.median(bkg.back())
            image.header['L1MEDIAN'] = (median_background,
                                        '[counts] Median of frame background')

            std_background = stats.robust_standard_deviation(bkg.back())
            image.header['L1SIGMA'] = (
                std_background, '[counts] Robust std dev of frame background')

            # Save some image statistics to the header
            good_objects = catalog['flag'] == 0
            for quantity in ['fwhm', 'ellipticity', 'theta']:
                good_objects = np.logical_and(
                    good_objects, np.logical_not(np.isnan(catalog[quantity])))
            if good_objects.sum() == 0:
                image.header['L1FWHM'] = ('NaN',
                                          '[arcsec] Frame FWHM in arcsec')
                image.header['L1ELLIP'] = ('NaN',
                                           'Mean image ellipticity (1-B/A)')
                image.header['L1ELLIPA'] = (
                    'NaN', '[deg] PA of mean image ellipticity')
            else:
                seeing = np.median(
                    catalog['fwhm'][good_objects]) * image.pixel_scale
                image.header['L1FWHM'] = (seeing,
                                          '[arcsec] Frame FWHM in arcsec')

                mean_ellipticity = stats.sigma_clipped_mean(
                    catalog['ellipticity'][good_objects], 3.0)
                image.header['L1ELLIP'] = (mean_ellipticity,
                                           'Mean image ellipticity (1-B/A)')

                mean_position_angle = stats.sigma_clipped_mean(
                    catalog['theta'][good_objects], 3.0)
                image.header['L1ELLIPA'] = (
                    mean_position_angle, '[deg] PA of mean image ellipticity')

            logging_tags = {
                key: float(image.header[key])
                for key in [
                    'L1MEAN', 'L1MEDIAN', 'L1SIGMA', 'L1FWHM', 'L1ELLIP',
                    'L1ELLIPA'
                ]
            }

            logger.info('Extracted sources',
                        image=image,
                        extra_tags=logging_tags)
            # adding catalog (a data table) to the appropriate images attribute.
            image.data_tables['catalog'] = DataTable(data_table=catalog,
                                                     name='CAT')
        except Exception:
            logger.error(logs.format_exception(), image=image)
        return image
Example #23
0
    def run(self):
        """ Runs the calibrating algorithm. The calibrated data is
            returned in self.dataout
        """
        ### Preparation
        binning = self.datain.getheadval('XBIN')
        ### Run Source Extractor
        # Make sure input data exists as file
        if not os.path.exists(self.datain.filename):
            self.datain.save()
        '''
        # Make catalog filename
        catfilename = self.datain.filenamebegin
        if catfilename[-1] in '._-': catfilename += 'sex_cat.fits'
        else: catfilename += '.sex_cat.fits'
        # Make background filename (may not be used - see below)
        bkgdfilename = self.datain.filenamebegin
        if bkgdfilename[-1] in '._-': bkgdfilename += 'SxBkgd.fits'
        else: bkgdfilename += '_SxBkgd.fits'
        self.log.debug('Sextractor catalog filename = %s' % catfilename)
		'''
        #Open data out of fits file for use in SEP
        image = self.datain.image

        #Set values for variables used later
        #These variables are used for the background analysis. bw and bh I found just testing various numbers
        maskthresh = 0.0
        bw, bh = 10, 10
        fw, fh = 3, 3
        fthresh = 0.0

        #Create the background image and it's error
        bkg = sep.Background(
            image,
            maskthresh=maskthresh,
            bw=bw,
            bh=bh,
            fw=fw,
            fh=fh,
            fthresh=fthresh)  #have sep determine the background of the image

        bkg_image = bkg.back()

        bkg_rms = bkg.rms()
        #Subtract the background from the image
        image_sub = image - bkg_image

        imsubmed = np.nanmedian(image_sub)
        imsubmad = mad_std(image_sub)

        #Create variables that are used during source Extraction
        extract_thresh = 5
        extract_err = bkg_rms

        #Extract sources from the subtracted image
        objects = sep.extract(image_sub, extract_thresh, err=extract_err)

        #Define variables used later during flux calculation
        sum_c = np.zeroes(len(objects))
        sum_c_err = np.zeroes(len(objects))
        sum_c_flags = np.zeroes(len(objects))
        ratio = np.zeroes(len(objects))
        rmax = np.zeroes(len(objects))
        dx = np.zeros(len(objects))
        dy = np.zeros(len(objects))

        #Do basic uncalibrated measurments of flux for use in step astrometry.

        #First we calculate flux using Ellipses. In order to do this we need to calculate the Kron Radius
        #For the ellipses Extract identified using the ellipse parameters it gives
        #R is equal to 6 as that is the default used in Source Extractor
        kronrad, krflag = sep.kron_radius(image_sub,
                                          objects['x'],
                                          objects['y'],
                                          objects['a'],
                                          objects['b'],
                                          objects['theta'],
                                          r=6)

        #Using this Kron radius we calculate the flux, this is equivallent to FLUX_AUTO in SExtractor
        flux_elip, fluxerr_elip, flag = sep.sum_ellipse(image_sub,
                                                        objects['x'],
                                                        objects['y'],
                                                        objects['a'],
                                                        objects['b'],
                                                        objects['theta'],
                                                        2.5 * kronrad,
                                                        err=bkg_rms,
                                                        subpix=1)

        #Then we calculate it using Circular Apetures, this will be used to remove sources that are too elipitical
        flux_circ, fluxerr_circ, flag = sep.sum_circle(image_sub,
                                                       objects['x'],
                                                       objects['y'],
                                                       r=2.5,
                                                       err=bkg_rms,
                                                       subpix=1)

        ### Extract catalog from source extractor and clean up dataset
        # Use catalog from sourse extrator (test.cat)
        #seo_catalog = astropy.table.Table.read(catfilename, format="fits", hdu='LDAC_OBJECTS')
        seo_Mag = -2.5 * np.log10(flux_elip)
        seo_MagErr = (2.5 / np.log(10) * (fluxerr_elip / flux_elip))

        # Select only the stars in the image: circular image and S/N > 10
        elongation = (flux_circ - flux_elip) < 250
        seo_SN = ((flux_elip / fluxerr_elip) > 10)
        seo_SN = (seo_SN) & (elongation) & (
            (flux_elip / fluxerr_elip) < 1000) & (fluxerr_elip != 0)
        self.log.debug('Selected %d stars from Source Extrator catalog' %
                       np.count_nonzero(seo_SN))

        ### Query and extract data from Guide Star Catalog
        # Get RA / Dec
        '''
        ra_center =  self.datain.getheadval('RA' ).split(':')
        dec_center = self.datain.getheadval('DEC').split(':')
        ra_cent =  ' '.join([str(s) for s in ra_center])
        dec_cent = ' '.join([str(s) for s in dec_center])
        center_coordinates = SkyCoord(ra_cent + ' ' + dec_cent, unit=(u.hourangle, u.deg) )
        self.log.debug('Using RA/Dec = %s / %s' % (center_coordinates.ra, center_coordinates.dec) )
        # Querry guide star catalog2 with center coordinates
        gsc2_query = 'http://gsss.stsci.edu/webservices/vo/CatalogSearch.aspx?'
        gsc2_query += 'RA='+str(center_coordinates.ra.value)
        gsc2_query += '&DEC='+str(center_coordinates.dec.value)
        gsc2_query += '&DSN=+&FORMAT=CSV&CAT=GSC241&SR=0.5&'
        self.log.debug('Running URL = %s' % gsc2_query)
        gsc2_result = requests.get(gsc2_query)
        # Get data from result
        filter_map = self.getarg('filtermap').split('|')
        filter_name = filter_tel = self.datain.getheadval('FILTER')
        for fil in filter_map:
            entry = fil.split('=')
            if entry[0] == filter_tel:
                try:
                    filter_name = entry[1]
                except:
                    self.log.error("Badly formatted filter mapping. No '=' after %s"
                                   % filter_tel)
        query_table = astropy.io.ascii.read(gsc2_result.text)
        table_filter = 'SDSS'+filter_name+'Mag'
        table_filter_err = 'SDSS'+filter_name+'MagErr'
        GSC_RA = query_table['ra'][(query_table[table_filter]<22) & (query_table[table_filter]>0)]
        GSC_DEC = query_table['dec'][(query_table[table_filter]<22) & (query_table[table_filter]>0)]
        GSC_Mag = query_table[table_filter][(query_table[table_filter]<22) & (query_table[table_filter]>0)]
        GSC_MagErr = query_table[table_filter_err][(query_table[table_filter]<22) & (query_table[table_filter]>0)]
        self.log.debug('Received %d entries from Guide Star Catalog' % len(GSC_RA))
        ### Mach Guide Star Catalog data with data from Source Extractor
        # Do the matching
        seo_radec = SkyCoord(ra=seo_catalog['ALPHA_J2000'], dec=seo_catalog['DELTA_J2000'])
        GSC_radec = SkyCoord(ra=GSC_RA*u.deg, dec=GSC_DEC*u.deg)
        idx, d2d, d3d = GSC_radec.match_to_catalog_sky(seo_radec[seo_SN])
        # only select objects less than 0.025 away in distance, get distance value
        dist_value = 1*0.76*binning/3600. #Maximum distance is 1 pixel
        mask = d2d.value<dist_value
        if(np.sum(mask) < 2):
            self.log.warn('Only %d sources match between image and guide star catalog, fit may not work' %
                          np.sum(mask) )
        self.log.debug('Distance_Value = %f, Min(distances) = %f, Mask length = %d' %
                       ( dist_value, np.min(d2d.value), np.sum(mask) ) )
        ### Calculate the fit correction between the guide star and the extracted values
        # Make lambda function to be minimized
        # The fit finds m_ml and b_ml where
        #     seo_Mag = b_ml + m_ml * GSC_Mag
        nll = lambda *args: -residual(*args)
        # Get errors
        eps_data = np.sqrt(GSC_MagErr**2+seo_MagErr[seo_SN][idx]**2)
        # Make estimate for intercept to give as initial guess
        b_ml0 = np.median(seo_Mag[seo_SN][idx][mask]-GSC_Mag[mask])
        self.log.debug('Offset guess is %f mag' % b_ml0)
        # Calculate distance from that guess and get StdDev of distances
        guessdistances = np.abs( b_ml0 - ( seo_Mag[seo_SN][idx] - GSC_Mag ) )
        guessdistmed = np.median(guessdistances[mask])
        # Update mask to ignore values with large STDEVS
        mask = np.logical_and( d2d.value < dist_value, guessdistances < 5 * guessdistmed )
        self.log.debug('Median of distance to guess = %f, Mask length = %d' %
                       ( guessdistmed, np.sum(mask) ) )
        # Solve linear equation
        result = scipy.optimize.minimize(nll, [1, b_ml0],
                                         args=(GSC_Mag[mask],
                                               seo_Mag[seo_SN][idx][mask],
                                               eps_data[mask]))
        m_ml, b_ml = result["x"]
        self.log.info('Fitted offset is %f mag, fitted slope is %f' % (b_ml, m_ml) )
        b_ml_corr = b_ml + (m_ml-1) * np.median(GSC_Mag[mask])
        self.log.info('Corrected offset is %f mag' % b_ml_corr)
        '''
        ### Make table with all data from source extractor
        # Collect data columns
        cols = []
        num = np.arange(1, len(objects['x']) + 1)
        cols.append(fits.Column(name='ID', format='D', array=num))
        cols.append(
            fits.Column(name='X',
                        format='D',
                        array=objects['x'][seo_SN],
                        unit='pixel'))
        cols.append(
            fits.Column(name='Y',
                        format='D',
                        array=objects['y'][seo_SN],
                        unit='pixel'))
        cols.append(
            fits.Column(name='Uncalibrated Magnitude',
                        format='D',
                        array=seo_Mag,
                        unit='magnitude'))
        cols.append(
            fits.Column(name='Uncalibrated Magnitude_Err',
                        format='D',
                        array=seo_MagErr,
                        unit='magnitude'))
        # Make table
        c = fits.ColDefs(cols)
        sources_table = fits.BinTableHDU.from_columns(c)
        '''
        ### Make table with data which was fit
        # Collect data columns
        cols = []
        cols.append(fits.Column(name='RA', format='D', array=GSC_RA[mask],
                                unit='deg'))
        cols.append(fits.Column(name='Dec', format='D', array=GSC_DEC[mask],
                                unit='deg'))
        cols.append(fits.Column(name='Diff_Deg', format='D', array=d2d[mask],
                                unit='deg'))
        cols.append(fits.Column(name='GSC_Mag', format='D',
                                array=GSC_Mag[mask], unit='magnitude'))
        cols.append(fits.Column(name='Img_Mag', format='D',
                                array=seo_Mag[seo_SN][idx][mask],
                                unit='magnitude'))
        cols.append(fits.Column(name='Error', format='D', array=eps_data[mask],
                                unit='magnitude'))
        # Make table
        c = fits.ColDefs(cols)
        fitdata_table = fits.BinTableHDU.from_columns(c)
        '''
        ### Make output data
        # Copy data from datain
        self.dataout = self.datain
        '''
        # Add Photometric Zero point magnitude
        self.dataout.setheadval('PHTZPRAW', -b_ml_corr, 'Photometric zeropoint for RAW data')
        self.dataout.setheadval('PTZRAWER', 0.0, 'Uncertainty of the RAW photometric zeropoint')
        self.dataout.setheadval('PHOTZP', 8.9,  'Photometric zeropoint MAG=-2.5*log(data)+PHOTZP')
        self.dataout.setheadval('BUNIT', 'Jy/pixel', 'Units for the data')
        '''
        # Scale the image using calculated b_ml_corr
        #image_background = fits.open(bkgdfilename)[0].data
        #bzero = np.nanpercentile(self.dataout.image,self.getarg('zeropercent'))
        #bzero = image_background
        #-- Alternative bzero idea:
        #-mask = image_array < np.percentile(image,90)
        #-bzero = np.median(image_array[mask])
        #bscale = 3631. * 10 ** (b_ml_corr/2.5)
        #self.dataout.image = bscale * (self.dataout.image - bzero)
        # Add sources and fitdata table
        self.dataout.tableset(sources_table.data, 'Sources',
                              sources_table.header)

        #self.dataout.tableset(fitdata_table.data,'Fit Data',fitdata_table.header)
        '''
        ### If requested make a plot of the fit and save as png
        if self.getarg('fitplot'):
            # Set up plot
            plt.figure(figsize=(10,7))
            # Plot 5sigma error range
            gmin = min(GSC_Mag[mask])
            gmax = max(GSC_Mag[mask])
            plt.fill([gmin,gmin,gmax,gmax],[gmin+b_ml0-guessdistmed, gmin+b_ml0+guessdistmed,
                                            gmax+b_ml0+guessdistmed, gmax+b_ml0-guessdistmed],'c')
            # Plot fits
            plt.plot(GSC_Mag[mask],m_ml*GSC_Mag[mask]+b_ml)
            plt.plot(GSC_Mag[mask],GSC_Mag[mask]+b_ml0)
            # Plot the datapoints
            plt.errorbar(GSC_Mag[d2d.value<dist_value],seo_Mag[seo_SN][idx][d2d.value<dist_value],
                         yerr=np.sqrt(eps_data[d2d.value<dist_value]**2),fmt='o',linestyle='none')
            plt.errorbar(GSC_Mag[mask],seo_Mag[seo_SN][idx][mask],
                         yerr=np.sqrt(eps_data[mask]**2),fmt='o',linestyle='none')
            #plt.plot(GSC_Mag[d2d.value<dist_value],m_ml*GSC_Mag[d2d.value<dist_value]+zeropoint_fit[1])
            plt.legend(['LM-fit','Fit-Guess','GuessDistMed Range','d<distval Data','Good Data'])
            plt.ylabel('Source extrator magnitude')
            plt.xlabel('Star catalog magnitude')
            plt.title('Calibration Fit for file\n' + os.path.split(self.dataout.filename)[1])
            # Plot the fit
            # Axis and labels
            # Save the image
            pngname = self.dataout.filenamebegin + 'FCALplot.png'
            plt.savefig(pngname)
            self.log.debug('Saved fit plot under %s' % pngname)
            '''
        ### If requested make a text file with the sources list
        if self.getarg('sourcetable'):

            # Save region file

            filename = self.dataout.filenamebegin + 'FCALsources.reg'
            with open(filename, 'w+') as f:
                f.write("# Region file format: DS9 version 4.1\n")
                f.write(
                    """global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1 image\n"""
                )
                for i in range(len(seo_catalog['x'][seo_SN])):
                    f.write("circle(%.7f,%.7f,0.005) # text={%i}\n" %
                            (seo_catalog['x'][seo_SN][i],
                             seo_catalog['y'][seo_SN][i], num[i]))

            # Save the table
            txtname = self.dataout.filenamebegin + 'FCALsources.txt'
            ascii.write(self.dataout.tableget('Sources'),
                        txtname,
                        format=self.getarg('sourcetableformat'))
            self.log.debug('Saved sources table under %s' % txtname)
Example #24
0
def aperture_photometry(img: Union[ndarray, MaskedArray], sources: ndarray,
                        background: Optional[Union[ndarray,
                                                   MaskedArray]] = None,
                        background_rms: Optional[Union[ndarray,
                                                       MaskedArray]] = None,
                        texp: float = 1,
                        gain: Union[float, ndarray, MaskedArray] = 1,
                        sat_level: float = 63000,
                        a: Optional[float] = None, b: Optional[float] = None,
                        theta: Optional[float] = 0,
                        a_in: Optional[float] = None,
                        a_out: Optional[float] = None,
                        b_out: Optional[float] = None,
                        theta_out: Optional[float] = None,
                        k: float = 0,
                        k_in: Optional[float] = None,
                        k_out: Optional[float] = None,
                        radius: float = 6,
                        fix_aper: bool = False,
                        fix_ell: bool = True,
                        fix_rot: bool = True,
                        apcorr_tol: float = 0.0001) -> ndarray:
    """
    Do automatic or fixed aperture photometry

    :param img: input 2D image array
    :param sources: record array of sources extracted with
        :func:`skylib.extraction.extract_sources`; should contain at least "x"
        and "y" columns
    :param background: optional sky background map; if omitted, extract
        background from the annulus around the aperture, see `a_in` below
    :param background_rms: optional sky background RMS map; if omitted,
        calculate RMS over the annulus around the aperture
    :param texp: exposure time in seconds
    :param gain: electrons to data units conversion factor; used to estimate
        photometric errors; for variable-gain images (e.g. mosaics), must be
        an array of the same shape as the input data
    :param sat_level: saturation level in ADUs; used to select only
        non-saturated stars for adaptive aperture photometry and aperture
        correction
    :param a: fixed aperture radius or semi-major axis in pixels; default: use
        automatic photometry with a = a_iso*k, where a_iso is the isophotal
        semi-major axis
    :param b: semi-minor axis in pixels when using a fixed aperture; default:
        same as `a`
    :param theta: rotation angle of semi-major axis in degrees CCW when using
        a fixed aperture and `b` != `a`; default: 0
    :param a_in: inner annulus radius or semi-major axis in pixels; used
        to estimate the background if `background` or `background_rms` are not
        provided, ignored otherwise; default: `a`*`k_in`
    :param a_out: outer annulus radius or semi-major axis in pixels; default:
        `a`*`k_out`
    :param b_out: outer annulus semi-minor axis in pixels; default: `b`*`k_out`
    :param theta_out: annulus orientation in degrees CCW; default: same
        as `theta`
    :param k: automatic aperture radius in units of isophotal radius; 0 means
        find the optimal radius based on SNR; default: 0
    :param k_in: inner annulus radius in units of aperture radius (fixed
        aperture, i.e. `a` is provided) or isophotal radius (adaptive aperture);
        default: 1.5*`k` or 3.75 if `k` is undefined and `a` = None
    :param k_out: outer annulus radius in units of aperture radius (fixed
        aperture) or isophotal radius (adaptive aperture); default: 2*`k` or
        5 if `k` is undefined and `a` = None
    :param radius: isophotal analysis radius in pixels used to compute automatic
        aperture if ellipse parameters (a,b,theta) are missing
    :param fix_aper: use the same aperture radius for all sources when doing
        automatic photometry; calculated as flux-weighted median of aperture
        sizes based on isophotal parameters
    :param fix_ell: use the same major to minor aperture axis ratio for all
        sources during automatic photometry; calculated as flux-weighted median
        of all ellipticities
    :param fix_rot: use the same aperture position angle for all sources during
        automatic photometry; calculated as flux-weighted median
        of all orientations
    :param apcorr_tol: growth curve stopping tolerance for aperture correction;
        0 = disable aperture correction

    :return: record array containing the input sources, with the following
        fields added or updated: "flux", "flux_err", "mag", "mag_err", "aper_a",
        "aper_b", "aper_theta", "aper_a_in", "aper_a_out", "aper_b_out",
        "aper_theta_out", "aper_area", "background_area", "background",
        "background_rms", "phot_flag"
    """
    if not len(sources):
        return array([])

    img = sep_compatible(img)

    texp = float(texp)
    if isscalar(gain):
        gain = float(gain)
    k = float(k)
    if k <= 0.1:
        k = 0  # temporary fix for k = 0 not being allowed in AgA
    if k_in:
        k_in = float(k_in)
    if k_out:
        k_out = float(k_out)

    x, y = sources['x'] - 1, sources['y'] - 1
    area_img = ones(img.shape, dtype=int32)
    if isinstance(img, MaskedArray):
        mask = img.mask
        img = img.data
    else:
        mask = None

    have_background = background is not None and background_rms is not None
    if have_background:
        background = sep_compatible(background)
        if isinstance(background, MaskedArray):
            if mask is None:
                mask = background.mask
            else:
                mask |= background.mask
            background = background.data

        background_rms = sep_compatible(background_rms)
        if isinstance(background_rms, MaskedArray):
            if mask is None:
                mask = background_rms.mask
            else:
                mask |= background_rms.mask
            background_rms = background_rms.data

    # Will need this to fill the newly added source table columns
    z = zeros(len(sources), float)

    fixed_aper = bool(a)
    if fixed_aper:
        # Use the same fixed aperture and annulus parameters for all sources
        a = float(a)
        if b:
            b = float(b)
        else:
            b = a
        if theta:
            theta = float(theta % 180)*pi/180
            if theta > pi/2:
                theta -= pi
        else:
            theta = 0

        if not have_background:
            if theta_out:
                theta_out = float(theta_out % 180)*pi/180
                if theta_out > pi/2:
                    theta_out -= pi
            elif theta_out != 0:
                theta_out = theta
            if a_in:
                a_in = float(a_in)
            else:
                a_in = a*k_in if k_in else a*1.5*k if k else a*3.75
            if a_out:
                a_out = float(a_out)
            else:
                a_out = a*k_out if k_out else a*2*k if k else a*5
            if b_out:
                b_out = float(b_out)
            else:
                b_out = a_out*b/a
    else:
        # Use automatic apertures derived from ellipse axes; will need image
        # with background subtracted
        if background is None:
            # Estimate background on the fly
            tmp_back, tmp_rms = estimate_background(img, size=64)
        else:
            tmp_back, tmp_rms = background, background_rms
        img_back = img - tmp_back
        for name in ['a', 'b', 'theta', 'flux']:
            if name not in sources.dtype.names:
                sources = append_fields(sources, name, z, usemask=False)
        a, b, theta = sources['a'], sources['b'], sources['theta']
        flux = sources['flux']
        bad = (a <= 0) | (b <= 0) | (flux <= 0)
        if bad.any():
            # Do isophotal analysis to compute ellipse parameters if missing
            yy, xx = indices(img.shape)
            for i in bad.nonzero()[0]:
                ap = (xx - sources[i]['x'])**2 + (yy - sources[i]['y'])**2 <= \
                    radius**2
                if ap.any():
                    yi, xi = ap.nonzero()
                    ap_data = img_back[ap].astype(float)
                    f = ap_data.sum()
                    if f > 0:
                        cx = (xi*ap_data).sum()/f
                        cy = (yi*ap_data).sum()/f
                        x2 = (xi**2*ap_data).sum()/f - cx**2
                        y2 = (yi**2*ap_data).sum()/f - cy**2
                        xy = (xi*yi*ap_data).sum()/f - cx*cy
                    else:
                        cx, cy = xi.mean(), yi.mean()
                        x2 = (xi**2).mean() - cx**2
                        y2 = (yi**2).mean() - cy**2
                        xy = (xi*yi).mean() - cx*cy
                    if x2 == y2:
                        thetai = 0
                    else:
                        thetai = arctan(2*xy/(x2 - y2))/2
                        if y2 > x2:
                            thetai += pi/2
                    m1 = (x2 + y2)/2
                    m2 = sqrt(max((x2 - y2)**2/4 + xy**2, 0))
                    ai = max(1/12, sqrt(max(m1 + m2, 0)))
                    bi = max(1/12, sqrt(max(m1 - m2, 0)))
                    if ai/bi > 2:
                        # Prevent too elongated apertures usually occurring for
                        # faint objects
                        bi = ai
                else:
                    # Cannot obtain a,b,theta from isophotal analysis, assume
                    # circular aperture
                    ai, bi, thetai, f = radius, radius, 0, 0
                a[i] = sources[i]['a'] = ai
                b[i] = sources[i]['b'] = bi
                theta[i] = sources[i]['theta'] = thetai
                flux[i] = sources[i]['flux'] = f
        bad = (a < b).nonzero()
        a[bad], b[bad] = b[bad], a[bad]
        theta[bad] += pi/2
        theta %= pi
        theta[theta > pi/2] -= pi
        elongation = a/b

        # Obtain the optimal aperture radius from the brightest non-saturated
        # source
        if not k:
            for i in argsort(flux)[::-1]:
                if sep.sum_ellipse(
                        img >= sat_level, [x[i]], [y[i]], a[i], b[i], theta[i],
                        1, subpix=0)[0][0]:
                    # Saturated source
                    continue
                try:
                    # noinspection PyTypeChecker
                    res = minimize(
                        calc_flux_err, [a[i]*1.6],
                        (img_back, x[i], y[i], elongation[i], theta[i], tmp_rms,
                         mask, gain), bounds=[(1, None)], tol=1e-5)
                except ValueError:
                    continue
                if not res.success:
                    continue
                k = res.x[0]/a[i]
                break
            if not k:
                raise ValueError(
                    'Not enough data for automatic aperture factor; use '
                    'explicit aperture factor')

        # Calculate weighted median of aperture sizes, elongations, and/or
        # orientations if requested
        r = sqrt(a*b)*k
        if r.size > 1 and any([fix_aper, fix_ell, fix_rot]):
            flux[flux < 0] = 0
            if not flux.any():
                raise ValueError(
                    'Not enough data for weighted median in fixed-aperture '
                    'automatic photometry; use static fixed-aperture or fully '
                    'adaptive automatic photometry instead')
            if fix_aper:
                r = weighted_median(r, flux)
            if fix_ell:
                elongation = weighted_median(elongation, flux)
            if fix_rot:
                theta = weighted_median(theta, flux, period=pi)
                if theta > pi/2:
                    theta -= pi

        # Calculate the final aperture and annulus sizes
        sqrt_el = sqrt(elongation)
        a, b = r*sqrt_el, r/sqrt_el
        if not have_background:
            if not k_in:
                k_in = 1.5*k
            if not k_out:
                k_out = 2*k
            a_in = a*(k_in/k)
            a_out, b_out = a*(k_out/k), b*(k_out/k)
            theta_out = theta

    # Calculate mean and RMS of background; to get the pure sigma, set error
    # to 1 and don't pass the gain
    if have_background:
        if fixed_aper and a == b:
            bk_area = sep.sum_circle(area_img, x, y, a, mask=mask, subpix=0)[0]
            bk_mean, bk_sigma = sep.sum_circle(
                background, x, y, a, err=1, mask=mask, subpix=0)[:2]
        else:
            bk_area = sep.sum_ellipse(
                area_img, x, y, a, b, theta, 1, mask=mask, subpix=0)[0]
            bk_mean, bk_sigma = sep.sum_ellipse(
                background, x, y, a, b, theta, 1, err=1, mask=mask,
                subpix=0)[:2]
        error = background_rms
    elif fixed_aper and a_out == b_out:
        bk_area = sep.sum_circann(
            area_img, x, y, a_in, a_out, mask=mask, subpix=0)[0]
        bk_mean, bk_sigma = sep.sum_circann(
            img, x, y, a_in, a_out, err=1, mask=mask, subpix=0)[:2]
        error = bk_sigma
    else:
        bk_area = sep.sum_ellipann(
            area_img, x, y, a_out, b_out, theta_out, a_in/a_out, 1, mask=mask,
            subpix=0)[0]
        bk_mean, bk_sigma = sep.sum_ellipann(
            img, x, y, a_out, b_out, theta_out, a_in/a_out, 1, err=1, mask=mask,
            subpix=0)[:2]
        error = bk_sigma

    if have_background:
        area = bk_area
    elif fixed_aper and a == b:
        area = sep.sum_circle(area_img, x, y, a, mask=mask, subpix=0)[0]
    else:
        area = sep.sum_ellipse(
            area_img, x, y, a, b, theta, 1, mask=mask, subpix=0)[0]

    if fixed_aper and a == b:
        # Fixed circular aperture
        if ndim(error) == 1:
            # Separate scalar error for each source
            flux, flux_err = empty([2, len(sources)], dtype=float)
            flags = empty(len(sources), dtype=int)
            for i, (_x, _y, _err) in enumerate(zip(x, y, error)):
                flux[i], flux_err[i], flags[i] = sep.sum_circle(
                    img, [_x], [_y], a, err=_err, mask=mask, gain=gain,
                    subpix=0)
        else:
            flux, flux_err, flags = sep.sum_circle(
                img, x, y, a, err=error, mask=mask, gain=gain, subpix=0)
    else:
        # Variable or elliptic aperture
        if ndim(error) == 1:
            # Separate scalar error for each source
            flux, flux_err = empty([2, len(sources)], dtype=float)
            flags = empty(len(sources), dtype=int)
            if isscalar(a):
                a = full_like(x, a)
            if isscalar(b):
                b = full_like(x, b)
            if isscalar(theta):
                theta = full_like(x, theta)
            for i, (_x, _y, _err, _a, _b, _theta) in enumerate(zip(
                    x, y, error, a, b, theta)):
                flux[i], flux_err[i], flags[i] = sep.sum_ellipse(
                    img, [_x], [_y], _a, _b, _theta, 1, err=_err, mask=mask,
                    gain=gain, subpix=0)
        else:
            flux, flux_err, flags = sep.sum_ellipse(
                img, x, y, a, b, theta, 1, err=error, mask=mask, gain=gain,
                subpix=0)

    # Convert background sum to mean and subtract background from fluxes
    if have_background:
        # Background area equals aperture area
        flux -= bk_mean
        bk_mean = bk_mean/area
    else:
        # Background area equals annulus area
        bk_mean = bk_mean/bk_area
        flux -= bk_mean*area

    # Convert ADUs to electrons
    flux *= gain
    flux_err *= gain
    bk_mean *= gain
    bk_sigma *= gain

    # Calculate aperture correction for all aperture sizes from the brightest
    # source
    aper_corr = {}
    if apcorr_tol > 0:
        for i in argsort(flux)[::-1]:
            xi, yi = x[i], y[i]
            if isscalar(a):
                ai = a
            else:
                ai = a[i]
            if isscalar(b):
                bi = b
            else:
                bi = b[i]
            if isscalar(theta):
                thetai = theta
            else:
                thetai = theta[i]
            if ndim(error) == 1:
                err = error[i]
            else:
                err = error

            if ai == bi:
                nsat = sep.sum_circle(
                    img >= sat_level, [xi], [yi], ai, subpix=0)[0][0]
            else:
                nsat = sep.sum_ellipse(
                    img >= sat_level, [xi], [yi], ai, bi, thetai, 1,
                    subpix=0)[0][0]
            if nsat:
                # Saturated source
                continue

            # Obtain total flux by increasing aperture size until it grows
            # either more than before (i.e. a nearby source in the aperture) or
            # less than the threshold (i.e. the growth curve reached saturation)
            f0 = f_prev = flux[i]
            dap = 0
            f_tot = df_prev = None
            while True:
                dap += 0.1
                if ai == bi:
                    f, f_err, fl = sep.sum_circle(
                        img, [xi], [yi], ai + dap, err=err, mask=mask,
                        gain=gain, subpix=0)
                    area_i = sep.sum_circle(
                        area_img, [xi], [yi], ai + dap, mask=mask,
                        subpix=0)[0][0]
                else:
                    f, f_err, fl = sep.sum_ellipse(
                        img, [xi], [yi], ai + dap, bi*(1 + dap/ai), thetai, 1,
                        err=err, mask=mask, gain=gain, subpix=0)
                    area_i = sep.sum_ellipse(
                        area_img, [xi], [yi], ai + dap, bi + dap, thetai, 1,
                        mask=mask, subpix=0)[0][0]
                f, fl = f[0], fl[0]
                if fl:
                    break
                f = (f - bk_mean[i]*area_i)*gain
                if f <= 0:
                    break
                df = f/f_prev
                if df_prev is not None and df > df_prev:
                    # Increasing growth, nearby source hit
                    f_tot = f_prev
                    break
                if df < 1 + apcorr_tol:
                    # Growth stopped to within the tolerance
                    f_tot = f
                    break
                f_prev, df_prev = f, df
            if f_tot is None:
                continue

            # Calculate fluxes for the chosen source for all unique aperture
            # sizes used for other sources
            fluxes_for_ap = {ai: f0}
            if not isscalar(a):
                for aj in set(a) - {ai}:
                    bj = aj*bi/ai
                    if aj == bj:
                        f, fl = sep.sum_circle(
                            img, [xi], [yi], aj, err=err, mask=mask, gain=gain,
                            subpix=0)[::2]
                        area_j = sep.sum_circle(
                            area_img, [xi], [yi], aj, mask=mask, subpix=0)[0][0]
                    else:
                        f, fl = sep.sum_ellipse(
                            img, [xi], [yi], aj, bj, thetai, 1, err=err,
                            mask=mask, gain=gain, subpix=0)[::2]
                        area_j = sep.sum_ellipse(
                            area_img, [xi], [yi], aj, bj, thetai, 1,
                            mask=mask, subpix=0)[0][0]
                    f, fl = f[0], fl[0]
                    f = (f - bk_mean[i]*area_j)*gain
                    if fl or f <= 0:
                        continue
                    fluxes_for_ap[aj] = f

            # Calculate aperture corrections
            for aj, f in fluxes_for_ap.items():
                if f < f_tot:
                    aper_corr[aj] = -2.5*log10(f_tot/f)

            break

    if 'flux' in sources.dtype.names:
        sources['flux'] = flux
    else:
        sources = append_fields(sources, 'flux', flux, usemask=False)

    if 'flux_err' in sources.dtype.names:
        sources['flux_err'] = flux_err
    else:
        sources = append_fields(sources, 'flux_err', flux_err, usemask=False)

    if 'flag' in sources.dtype.names:
        sources['flag'] |= flags
    else:
        sources = append_fields(sources, 'flag', flags, usemask=False)

    for name in ['mag', 'mag_err', 'aper_a', 'aper_b', 'aper_theta',
                 'aper_a_in', 'aper_a_out', 'aper_b_out', 'aper_theta_out',
                 'aper_area', 'background_area', 'background',
                 'background_rms']:
        if name not in sources.dtype.names:
            sources = append_fields(sources, name, z, usemask=False)

    good = (flux > 0).nonzero()
    if len(good[0]):
        sources['mag'][good] = -2.5*log10(flux[good]/texp)
        sources['mag_err'][good] = 2.5*log10(1 + flux_err[good]/flux[good])

    sources['aper_a'] = a
    sources['aper_b'] = b
    sources['aper_theta'] = theta

    if not have_background:
        sources['aper_a_in'] = a_in
        sources['aper_a_out'] = a_out
        sources['aper_b_out'] = b_out
        sources['aper_theta_out'] = theta_out

    sources['aper_area'] = area
    sources['background_area'] = bk_area
    sources['background'] = bk_mean
    sources['background_rms'] = bk_sigma

    # Apply aperture correction
    for i in good[0]:
        sources['mag'][i] += aper_corr.get(sources['aper_a'][i], 0)

    return sources
Example #25
0
def aperture_masses(info,
                    mass_map,
                    detect=None,
                    rad=None,
                    n_rad=15,
                    linear=False,
                    r_min=0.1,
                    r_max=None,
                    subpix=5,
                    **detect_kwargs):
    """Estimate stellar mass profiles based on aperture statistics.

    Parameters
    ----------
    info : dict
        Basic information of the galaxy.
    maps : dict
        All the stellar mass maps.
    detect : dict
        A dictionary that contains the average shape of the galaxy. Default: None.
    rad : ndarray, optional
        Array of boundaries for radius bins in unit of kpc. Default: None.
    n_rad : int, optional
        Number of radial bins. Default: 15.
    linear : bool, optional
        If True, radial bins will be uniformly spaced in linear space.
        If False, radial bins will be uniformly spaced in log10 space.
        Default: False
    r_min : float, optional
        Minimum radius of the radial bins. Default: 0.1
    r_max : float, optional
        Maximum radius of the radial bins. Default: None.
    subpix : int, optional
        Subpixel sampling factor. Default is 5.

    Returns
    -------

    """
    # If basic information is not available, detect the galaxy here.
    if detect is None:
        detect = detect_galaxy(info, mass_map, **detect_kwargs)

    # Get the radial bins in unit of kpc
    if rad is None:
        if r_max is None:
            r_max = info['img_w'] / 2.0
        if linear:
            rad = np.linspace(r_min, r_max * info['pix'], (n_rad + 1))
        else:
            rad = np.logspace(np.log10(r_min), np.log10(r_max * info['pix']),
                              (n_rad + 1))

    # Mass within different apertures
    maper = sep.sum_ellipse(mass_map,
                            detect['x'],
                            detect['y'],
                            rad / info['pix'],
                            rad / info['pix'] * detect['ba'],
                            detect['theta'],
                            1.0,
                            bkgann=None,
                            subpix=subpix)[0]

    return rad, maper
Example #26
0
    def do_stage(self, images):
        for i, image in enumerate(images):
            try:
                # Set the number of source pixels to be 5% of the total. This keeps us safe from
                # satellites and airplanes.
                sep.set_extract_pixstack(int(image.nx * image.ny * 0.05))

                data = image.data.copy()
                error = (np.abs(data) + image.readnoise**2.0)**0.5
                mask = image.bpm > 0

                # Fits can be backwards byte order, so fix that if need be and subtract
                # the background
                try:
                    bkg = sep.Background(data,
                                         mask=mask,
                                         bw=32,
                                         bh=32,
                                         fw=3,
                                         fh=3)
                except ValueError:
                    data = data.byteswap(True).newbyteorder()
                    bkg = sep.Background(data,
                                         mask=mask,
                                         bw=32,
                                         bh=32,
                                         fw=3,
                                         fh=3)
                bkg.subfrom(data)

                # Do an initial source detection
                # TODO: Add back in masking after we are sure SEP works
                sources = sep.extract(data,
                                      self.threshold,
                                      minarea=self.min_area,
                                      err=error,
                                      deblend_cont=0.005)

                # Convert the detections into a table
                sources = Table(sources)

                # Calculate the ellipticity
                sources['ellipticity'] = 1.0 - (sources['b'] / sources['a'])

                # Fix any value of theta that are invalid due to floating point rounding
                # -pi / 2 < theta < pi / 2
                sources['theta'][sources['theta'] > (np.pi / 2.0)] -= np.pi
                sources['theta'][sources['theta'] < (-np.pi / 2.0)] += np.pi

                # Calculate the kron radius
                kronrad, krflag = sep.kron_radius(data, sources['x'],
                                                  sources['y'], sources['a'],
                                                  sources['b'],
                                                  sources['theta'], 6.0)
                sources['flag'] |= krflag
                sources['kronrad'] = kronrad

                # Calcuate the equivilent of flux_auto
                flux, fluxerr, flag = sep.sum_ellipse(data,
                                                      sources['x'],
                                                      sources['y'],
                                                      sources['a'],
                                                      sources['b'],
                                                      np.pi / 2.0,
                                                      2.5 * kronrad,
                                                      subpix=1,
                                                      err=error)
                sources['flux'] = flux
                sources['fluxerr'] = fluxerr
                sources['flag'] |= flag

                # Calculate the FWHMs of the stars:
                fwhm = 2.0 * (np.log(2) *
                              (sources['a']**2.0 + sources['b']**2.0))**0.5
                sources['fwhm'] = fwhm

                # Cut individual bright pixels. Often cosmic rays
                sources = sources[fwhm > 1.0]

                # Measure the flux profile
                flux_radii, flag = sep.flux_radius(data,
                                                   sources['x'],
                                                   sources['y'],
                                                   6.0 * sources['a'],
                                                   [0.25, 0.5, 0.75],
                                                   normflux=sources['flux'],
                                                   subpix=5)
                sources['flag'] |= flag
                sources['fluxrad25'] = flux_radii[:, 0]
                sources['fluxrad50'] = flux_radii[:, 1]
                sources['fluxrad75'] = flux_radii[:, 2]

                # Calculate the windowed positions
                sig = 2.0 / 2.35 * sources['fluxrad50']
                xwin, ywin, flag = sep.winpos(data, sources['x'], sources['y'],
                                              sig)
                sources['flag'] |= flag
                sources['xwin'] = xwin
                sources['ywin'] = ywin

                # Calculate the average background at each source
                bkgflux, fluxerr, flag = sep.sum_ellipse(bkg.back(),
                                                         sources['x'],
                                                         sources['y'],
                                                         sources['a'],
                                                         sources['b'],
                                                         np.pi / 2.0,
                                                         2.5 *
                                                         sources['kronrad'],
                                                         subpix=1)
                #masksum, fluxerr, flag = sep.sum_ellipse(mask, sources['x'], sources['y'],
                #                                         sources['a'], sources['b'], np.pi / 2.0,
                #                                         2.5 * kronrad, subpix=1)

                background_area = (
                    2.5 * sources['kronrad']
                )**2.0 * sources['a'] * sources['b'] * np.pi  # - masksum
                sources['background'] = bkgflux
                sources['background'][background_area > 0] /= background_area[
                    background_area > 0]
                # Update the catalog to match fits convention instead of python array convention
                sources['x'] += 1.0
                sources['y'] += 1.0

                sources['xpeak'] += 1
                sources['ypeak'] += 1

                sources['xwin'] += 1.0
                sources['ywin'] += 1.0

                sources['theta'] = np.degrees(sources['theta'])

                image.catalog = sources['x', 'y', 'xwin', 'ywin', 'xpeak',
                                        'ypeak', 'flux', 'fluxerr',
                                        'background', 'fwhm', 'a', 'b',
                                        'theta', 'kronrad', 'ellipticity',
                                        'fluxrad25', 'fluxrad50', 'fluxrad75',
                                        'x2', 'y2', 'xy', 'flag']

                # Add the units and description to the catalogs
                image.catalog['x'].unit = 'pixel'
                image.catalog['x'].description = 'X coordinate of the object'
                image.catalog['y'].unit = 'pixel'
                image.catalog['y'].description = 'Y coordinate of the object'
                image.catalog['xwin'].unit = 'pixel'
                image.catalog[
                    'xwin'].description = 'Windowed X coordinate of the object'
                image.catalog['ywin'].unit = 'pixel'
                image.catalog[
                    'ywin'].description = 'Windowed Y coordinate of the object'
                image.catalog['xpeak'].unit = 'pixel'
                image.catalog['xpeak'].description = 'X coordinate of the peak'
                image.catalog['ypeak'].unit = 'pixel'
                image.catalog[
                    'ypeak'].description = 'Windowed Y coordinate of the peak'
                image.catalog['flux'].unit = 'counts'
                image.catalog[
                    'flux'].description = 'Flux within a Kron-like elliptical aperture'
                image.catalog['fluxerr'].unit = 'counts'
                image.catalog[
                    'fluxerr'].description = 'Erronr on the flux within a Kron-like elliptical aperture'
                image.catalog['background'].unit = 'counts'
                image.catalog[
                    'background'].description = 'Average background value in the aperture'
                image.catalog['fwhm'].unit = 'pixel'
                image.catalog['fwhm'].description = 'FWHM of the object'
                image.catalog['a'].unit = 'pixel'
                image.catalog[
                    'a'].description = 'Semi-major axis of the object'
                image.catalog['b'].unit = 'pixel'
                image.catalog[
                    'b'].description = 'Semi-minor axis of the object'
                image.catalog['theta'].unit = 'degrees'
                image.catalog[
                    'theta'].description = 'Position angle of the object'
                image.catalog['kronrad'].unit = 'pixel'
                image.catalog[
                    'kronrad'].description = 'Kron radius used for extraction'
                image.catalog['ellipticity'].description = 'Ellipticity'
                image.catalog['fluxrad25'].unit = 'pixel'
                image.catalog[
                    'fluxrad25'].description = 'Radius containing 25% of the flux'
                image.catalog['fluxrad50'].unit = 'pixel'
                image.catalog[
                    'fluxrad50'].description = 'Radius containing 50% of the flux'
                image.catalog['fluxrad75'].unit = 'pixel'
                image.catalog[
                    'fluxrad75'].description = 'Radius containing 75% of the flux'
                image.catalog['x2'].unit = 'pixel^2'
                image.catalog[
                    'x2'].description = 'Variance on X coordinate of the object'
                image.catalog['y2'].unit = 'pixel^2'
                image.catalog[
                    'y2'].description = 'Variance on Y coordinate of the object'
                image.catalog['xy'].unit = 'pixel^2'
                image.catalog['xy'].description = 'XY covariance of the object'
                image.catalog[
                    'flag'].description = 'Bit mask combination of extraction and photometry flags'

                image.catalog.sort('flux')
                image.catalog.reverse()

                logging_tags = logs.image_config_to_tags(
                    image, self.group_by_keywords)
                logs.add_tag(logging_tags, 'filename',
                             os.path.basename(image.filename))

                # Save some background statistics in the header
                mean_background = stats.sigma_clipped_mean(bkg.back(), 5.0)
                image.header['L1MEAN'] = (
                    mean_background,
                    '[counts] Sigma clipped mean of frame background')
                logs.add_tag(logging_tags, 'L1MEAN', float(mean_background))

                median_background = np.median(bkg.back())
                image.header['L1MEDIAN'] = (
                    median_background, '[counts] Median of frame background')
                logs.add_tag(logging_tags, 'L1MEDIAN',
                             float(median_background))

                std_background = stats.robust_standard_deviation(bkg.back())
                image.header['L1SIGMA'] = (
                    std_background,
                    '[counts] Robust std dev of frame background')
                logs.add_tag(logging_tags, 'L1SIGMA', float(std_background))

                # Save some image statistics to the header
                good_objects = image.catalog['flag'] == 0

                seeing = np.median(
                    image.catalog['fwhm'][good_objects]) * image.pixel_scale
                image.header['L1FWHM'] = (seeing,
                                          '[arcsec] Frame FWHM in arcsec')
                logs.add_tag(logging_tags, 'L1FWHM', float(seeing))

                mean_ellipticity = stats.sigma_clipped_mean(
                    sources['ellipticity'][good_objects], 3.0)
                image.header['L1ELLIP'] = (mean_ellipticity,
                                           'Mean image ellipticity (1-B/A)')
                logs.add_tag(logging_tags, 'L1ELLIP', float(mean_ellipticity))

                mean_position_angle = stats.sigma_clipped_mean(
                    sources['theta'][good_objects], 3.0)
                image.header['L1ELLIPA'] = (
                    mean_position_angle, '[deg] PA of mean image ellipticity')
                logs.add_tag(logging_tags, 'L1ELLIPA',
                             float(mean_position_angle))

                self.logger.info('Extracted sources', extra=logging_tags)

            except Exception as e:
                logging_tags = logs.image_config_to_tags(
                    image, self.group_by_keywords)
                logs.add_tag(logging_tags, 'filename',
                             os.path.basename(image.filename))
                self.logger.error(e, extra=logging_tags)
        return images
Example #27
0
    def _run_sep(self):
        import sep
        # THRESH=1.2 # in sky sigma
        # DETECT_THRESH=1.6 # in sky sigma
        # DEBLEND_MINCONT=0.005
        # DETECT_MINAREA  = 6 # minimum number of pixels above threshold
        objs, seg = sep.extract(
            self.detim,
            self.detect_thresh,
            err=self.detnoise,
            segmentation_map=True,
            **self.sx_config
        )

        logger.debug('found %d objects' % objs.size)
        if objs.size == 0:
            self.cat = objs
            return None

        flux_auto = np.zeros(objs.size)-9999.0
        fluxerr_auto = np.zeros(objs.size)-9999.0
        flux_radius = np.zeros(objs.size)-9999.0
        kron_radius = np.zeros(objs.size)-9999.0

        w, = np.where(
            (objs['a'] >= 0.0) &
            (objs['b'] >= 0.0) &
            between(objs['theta'], -pi/2., pi/2., type='[]')
        )

        if w.size > 0:
            kron_radius[w], krflag = sep.kron_radius(
                self.detim,
                objs['x'][w],
                objs['y'][w],
                objs['a'][w],
                objs['b'][w],
                objs['theta'][w],
                6.0,
            )
            objs['flag'][w] |= krflag

            aper_rad = 2.5*kron_radius
            flux_auto[w], fluxerr_auto[w], flag_auto = \
                sep.sum_ellipse(
                    self.detim,
                    objs['x'][w],
                    objs['y'][w],
                    objs['a'][w],
                    objs['b'][w],
                    objs['theta'][w],
                    aper_rad[w],
                    subpix=1,
                )
            objs['flag'][w] |= flag_auto

            # what we did in DES, but note threshold above
            # is 1 as opposed to wide survey. deep survey
            # was even lower, 0.8?

            # used half light radius
            PHOT_FLUXFRAC = 0.5

            flux_radius[w], frflag = sep.flux_radius(
                self.detim,
                objs['x'][w],
                objs['y'][w],
                6.*objs['a'][w],
                PHOT_FLUXFRAC,
                normflux=flux_auto[w],
                subpix=5,
            )
            objs['flag'][w] |= frflag  # combine flags into 'flag'

        ncut = 2  # need this to make sure array
        new_dt = [
            ('id', 'i8'),
            ('number', 'i4'),
            ('ncutout', 'i4'),
            ('kron_radius', 'f4'),
            ('flux_auto', 'f4'),
            ('fluxerr_auto', 'f4'),
            ('flux_radius', 'f4'),
            ('isoarea_image', 'f4'),
            ('iso_radius', 'f4'),
            ('box_size', 'i4'),
            ('file_id', 'i8', ncut),
            ('orig_row', 'f4', ncut),
            ('orig_col', 'f4', ncut),
            ('orig_start_row', 'i8', ncut),
            ('orig_start_col', 'i8', ncut),
            ('orig_end_row', 'i8', ncut),
            ('orig_end_col', 'i8', ncut),
            ('cutout_row', 'f4', ncut),
            ('cutout_col', 'f4', ncut),
            ('dudrow', 'f8', ncut),
            ('dudcol', 'f8', ncut),
            ('dvdrow', 'f8', ncut),
            ('dvdcol', 'f8', ncut),
        ]

        cat = eu.numpy_util.add_fields(objs, new_dt)
        cat['id'] = np.arange(cat.size)
        cat['number'] = np.arange(1, cat.size+1)
        cat['ncutout'] = 1
        cat['flux_auto'] = flux_auto
        cat['fluxerr_auto'] = fluxerr_auto
        cat['flux_radius'] = flux_radius
        wcs = self.datalist[0]['wcs']
        cat['dudrow'][:, 0] = wcs.dudy
        cat['dudcol'][:, 0] = wcs.dudx
        cat['dvdrow'][:, 0] = wcs.dvdy
        cat['dvdcol'][:, 0] = wcs.dvdx

        # use the number of pixels in the seg map as the iso area
        for i in range(objs.size):
            w = np.where(seg == (i+1))
            cat['isoarea_image'][i] = w[0].size

        cat['iso_radius'] = np.sqrt(cat['isoarea_image'].clip(min=1)/np.pi)

        box_size = self._get_box_sizes(cat)

        half_box_size = box_size//2

        maxrow, maxcol = self.detim.shape

        cat['box_size'] = box_size

        cat['orig_row'][:, 0] = cat['y']
        cat['orig_col'][:, 0] = cat['x']

        orow = cat['orig_row'][:, 0].astype('i4')
        ocol = cat['orig_col'][:, 0].astype('i4')

        ostart_row = orow - half_box_size + 1
        ostart_col = ocol - half_box_size + 1
        oend_row = orow + half_box_size + 1  # plus one for slices
        oend_col = ocol + half_box_size + 1

        ostart_row.clip(min=0, out=ostart_row)
        ostart_col.clip(min=0, out=ostart_col)
        oend_row.clip(max=maxrow, out=oend_row)
        oend_col.clip(max=maxcol, out=oend_col)

        # could result in smaller than box_size above
        cat['orig_start_row'][:, 0] = ostart_row
        cat['orig_start_col'][:, 0] = ostart_col
        cat['orig_end_row'][:, 0] = oend_row
        cat['orig_end_col'][:, 0] = oend_col
        cat['cutout_row'][:, 0] = \
            cat['orig_row'][:, 0] - cat['orig_start_row'][:, 0]
        cat['cutout_col'][:, 0] = \
            cat['orig_col'][:, 0] - cat['orig_start_col'][:, 0]

        self.seg = seg
        self.bmask = np.zeros(seg.shape, dtype='i4')
        self.cat = cat
Example #28
0
def test_vs_sextractor():
    data = np.copy(image_data)  # make an explicit copy so we can 'subfrom'
    bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3)

    # Test that SExtractor background is same as SEP:
    bkgarr = bkg.back(dtype=np.float32)
    assert_allclose(bkgarr, image_refback, rtol=1.e-5)

    # Extract objects
    bkg.subfrom(data)
    objs = sep.extract(data, 1.5*bkg.globalrms)
    objs = np.sort(objs, order=['y'])

    # Read SExtractor result
    refobjs = np.loadtxt(IMAGECAT_FNAME, dtype=IMAGECAT_DTYPE)
    refobjs = np.sort(refobjs, order=['y'])

    # Found correct number of sources at the right locations?
    assert_allclose(objs['x'], refobjs['x'] - 1., atol=1.e-3)
    assert_allclose(objs['y'], refobjs['y'] - 1., atol=1.e-3)

    # Test aperture flux
    flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 5.,
                                         err=bkg.globalrms)
    assert_allclose(flux, refobjs['flux_aper'], rtol=2.e-4)
    assert_allclose(fluxerr, refobjs['fluxerr_aper'], rtol=1.0e-5)

    # check if the flags work at all (comparison values 
    assert ((flag & sep.APER_TRUNC) != 0).sum() == 4
    assert ((flag & sep.APER_HASMASKED) != 0).sum() == 0

    # Test "flux_auto"
    kr, flag = sep.kron_radius(data, objs['x'], objs['y'], objs['a'],
                               objs['b'], objs['theta'], 6.0)

    flux, fluxerr, flag = sep.sum_ellipse(data, objs['x'], objs['y'],
                                          objs['a'], objs['b'],
                                          objs['theta'], r=2.5 * kr,
                                          err=bkg.globalrms, subpix=1)

    # For some reason, object at index 59 doesn't match. It's very small
    # and kron_radius is set to 0.0 in SExtractor, but 0.08 in sep.
    # Most of the other values are within 1e-4 except one which is only
    # within 0.01. This might be due to a change in SExtractor between
    # v2.8.6 (used to generate "truth" catalog) and v2.18.11.
    kr[59] = 0.0
    flux[59] = 0.0
    fluxerr[59] = 0.0
    assert_allclose(2.5*kr, refobjs['kron_radius'], rtol=0.01)
    assert_allclose(flux, refobjs['flux_auto'], rtol=0.01)
    assert_allclose(fluxerr, refobjs['fluxerr_auto'], rtol=0.01)

    # Test ellipse representation conversion
    cxx, cyy, cxy = sep.ellipse_coeffs(objs['a'], objs['b'], objs['theta'])
    assert_allclose(cxx, objs['cxx'], rtol=1.e-4)
    assert_allclose(cyy, objs['cyy'], rtol=1.e-4)
    assert_allclose(cxy, objs['cxy'], rtol=1.e-4)

    a, b, theta = sep.ellipse_axes(objs['cxx'], objs['cyy'], objs['cxy'])
    assert_allclose(a, objs['a'], rtol=1.e-4)
    assert_allclose(b, objs['b'], rtol=1.e-4)
    assert_allclose(theta, objs['theta'], rtol=1.e-4)

    #test round trip
    cxx, cyy, cxy = sep.ellipse_coeffs(a, b, theta)
    assert_allclose(cxx, objs['cxx'], rtol=1.e-4)
    assert_allclose(cyy, objs['cyy'], rtol=1.e-4)
    assert_allclose(cxy, objs['cxy'], rtol=1.e-4)

    # test flux_radius
    fr, flags = sep.flux_radius(data, objs['x'], objs['y'], 6.*refobjs['a'],
                                [0.1, 0.5, 0.6], normflux=refobjs['flux_auto'],
                                subpix=5)
    assert_allclose(fr, refobjs["flux_radius"], rtol=0.04, atol=0.01)

    # test winpos
    sig = 2. / 2.35 * fr[:, 1]  # flux_radius = 0.5
    xwin, ywin, flag = sep.winpos(data, objs['x'], objs['y'], sig)
    assert_allclose(xwin, refobjs["xwin"] - 1., rtol=0., atol=0.0025)
    assert_allclose(ywin, refobjs["ywin"] - 1., rtol=0., atol=0.0025)
Example #29
0
naper = 1000
nloop = 1
a = 1.
b = 1.
theta = np.pi / 4.

for r in r_list:
    for subpix, method, label in subpix_list:
        line = "| ellipses r={0:2d}  {1:8s} |".format(int(r), label)

        t0 = time.time()
        flux, fluxerr, flag = sep.sum_ellipse(data,
                                              x,
                                              y,
                                              a,
                                              b,
                                              theta,
                                              r,
                                              subpix=subpix)
        t1 = time.time()
        t_sep = (t1 - t0) * 1.e6 / naper / nloop
        line += " {0:7.2f} us/aper |".format(t_sep)

        if HAVE_PHOTUTILS:
            apertures = photutils.EllipticalAperture((x, y), a * r, b * r,
                                                     theta)
            t0 = time.time()
            res = photutils.aperture_photometry(data,
                                                apertures,
                                                method=method,
                                                subpixels=subpix)
Example #30
0
def flux_radius(components, observation=None, frac=0.5, weight_order=0):
    """
    Determine the radius R (in pixels, along semi-major axis), 
    the flux within R has a fraction of `frac` over the total flux.

    Parameters
    ----------
    components: a list of `scarlet.Component` or `scarlet.ComponentTree`
        Component to analyze
    observation: 

    frac: float
        fraction of lights within this R.

    """
    from scipy.interpolate import interp1d, UnivariateSpline

    if not isinstance(components, list):
        components = [components]

    # Determine the centroid, averaged through channels
    _, y_cen, x_cen = centroid(components, observation=observation)
    s = shape(components,
              observation,
              show_fig=False,
              weight_order=weight_order)
    q = s['q']
    theta = np.deg2rad(s['pa'])

    blend = scarlet.Blend(components, observation)
    model = blend.get_model()
    mask = (observation.weights == 0)
    model = model * ~mask

    total_flux = model.sum(axis=(1, 2))

    depth = model.shape[0]
    r_frac = []

    ## sep.sum_ellipse is very slow! Try to improve!
    if depth > 1:
        for i in range(depth):
            r_max = max(model.shape)
            r_ = np.linspace(0, r_max, 500)
            flux_ = sep.sum_ellipse(model[i],
                                    x_cen,
                                    y_cen,
                                    1,
                                    1 * q[i],
                                    theta[i],
                                    r=r_)[0]
            flux_ /= total_flux[i]
            func = UnivariateSpline(r_, flux_ - frac, s=0)
            r_frac.append(func.roots()[0])
    else:  # might be buggy
        r_max = max(model.shape)
        r_ = np.linspace(0, r_max, 500)
        flux_ = sep.sum_ellipse(model[0],
                                x_cen,
                                y_cen,
                                1,
                                1 * q[0],
                                theta[0],
                                r=r_)[0]
        flux_ /= total_flux[0]
        func = UnivariateSpline(r_, flux_ - frac, s=0)
        r_frac.append(func.roots()[0])

    return np.array(r_frac)
Example #31
0
def detect_with_sep(event,
                    detect_thresh=2.,
                    npixels=8,
                    grow_seg=5,
                    gauss_fwhm=2.,
                    gsize=3,
                    im_wcs=None,
                    root='mycat'):

    drz_file = event['fits_s3_key']
    drz_file_bucket = event['fits_s3_bucket']
    root = drz_file.split('/')[-1].split('_')[0]

    s3 = boto3.resource('s3')
    s3_client = boto3.client('s3')
    bkt = s3.Bucket(drz_file_bucket)
    bkt.download_file(drz_file,
                      '/tmp/{0}'.format(root),
                      ExtraArgs={"RequestPayer": "requester"})

    im = fits.open('/tmp/{0}'.format(root))
    im_wcs = wcs.WCS(im[1].header, relax=True)

    data = im[1].data.byteswap().newbyteorder()
    wht_data = im[2].data.byteswap().newbyteorder()
    data_mask = np.cast[data.dtype](data == 0)

    ## Get AB zeropoint
    if 'PHOTFNU' in im[0].header:
        ZP = -2.5 * np.log10(im[0].header['PHOTFNU']) + 8.90
    elif 'PHOTFLAM' in im[0].header:
        ZP = (-2.5 * np.log10(im[0].header['PHOTFLAM']) - 21.10 -
              5 * np.log10(im[0].header['PHOTPLAM']) + 18.6921)
    else:
        print(
            'Couldn\'t find PHOTFNU or PHOTPLAM/PHOTFLAM keywords, use ZP=25')
        return None

    # Scale fluxes to mico-Jy
    uJy_to_dn = 1 / (3631 * 1e6 * 10**(-0.4 * ZP))

    err = 1 / np.sqrt(wht_data)

    # set up the error array
    err = 1 / np.sqrt(wht_data)
    err[~np.isfinite(err)] = 0
    mask = (err == 0)

    # get the background
    bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
    bkg_data = bkg.back()

    ratio = bkg.rms() / err
    err_scale = np.median(ratio[(~mask) & np.isfinite(ratio)])

    err *= err_scale

    objects = sep.extract(data - bkg_data,
                          detect_thresh,
                          err=err,
                          mask=mask,
                          minarea=14,
                          filter_kernel=GAUSS_3_7x7,
                          filter_type='conv',
                          deblend_nthresh=32,
                          deblend_cont=0.005,
                          clean=True,
                          clean_param=1.,
                          segmentation_map=False)

    catalog = Table(objects)

    # add things to catalog

    autoparams = [2.5, 3.5]

    catalog['number'] = np.arange(len(catalog), dtype=np.int32) + 1
    catalog['theta'] = np.clip(catalog['theta'], -np.pi / 2, np.pi / 2)
    for c in ['a', 'b', 'x', 'y', 'theta']:
        catalog = catalog[np.isfinite(catalog[c])]

    catalog['ra'], catalog['dec'] = im_wcs.all_pix2world(
        catalog['x'], catalog['y'], 1)
    catalog['ra'].unit = u.deg
    catalog['dec'].unit = u.deg
    catalog['x_world'], catalog['y_world'] = catalog['ra'], catalog['dec']

    kronrad, krflag = sep.kron_radius(data - bkg_data, catalog['x'],
                                      catalog['y'], catalog['a'], catalog['b'],
                                      catalog['theta'], 6.0)

    kronrad *= autoparams[0]
    kronrad[~np.isfinite(kronrad)] = autoparams[1]
    kronrad = np.maximum(kronrad, autoparams[1])

    kron_out = sep.sum_ellipse(data - bkg_data,
                               catalog['x'],
                               catalog['y'],
                               catalog['a'],
                               catalog['b'],
                               catalog['theta'],
                               kronrad,
                               subpix=5,
                               err=err)

    kron_flux, kron_fluxerr, kron_flag = kron_out
    kron_flux_flag = kron_flag

    catalog['mag_auto_raw'] = ZP - 2.5 * np.log10(kron_flux)
    catalog['magerr_auto_raw'] = 2.5 / np.log(10) * kron_fluxerr / kron_flux

    catalog['mag_auto'] = catalog['mag_auto_raw'] * 1.
    catalog['magerr_auto'] = catalog['magerr_auto_raw'] * 1.

    catalog['kron_radius'] = kronrad * u.pixel
    catalog['kron_flag'] = krflag
    catalog['kron_flux_flag'] = kron_flux_flag

    # Make a plot
    im_data = im[1].data
    im_shape = im_data.shape
    im_data[np.isnan(im_data)] = 0.0

    # Trim the top and bottom 1 percent of pixel values
    top = np.percentile(im_data, 99)
    im_data[im_data > top] = top
    bottom = np.percentile(im_data, 1)
    im_data[im_data < bottom] = bottom

    # Scale the data.
    im_data = im_data - im_data.min()
    im_data = (im_data / im_data.max()) * 255.
    im_data = np.uint8(im_data)

    f, (ax) = plt.subplots(1, 1, sharex=True)
    f.set_figheight(12)
    f.set_figwidth(12)
    ax.imshow(im_data, cmap="Greys", clim=(0, 255), origin='lower')
    ax.plot(catalog['x'],
            catalog['y'],
            'o',
            markeredgewidth=1,
            markeredgecolor='red',
            markerfacecolor='None')
    ax.set_xlim([-0.05 * im_shape[1], 1.05 * im_shape[1]])
    ax.set_ylim([-0.05 * im_shape[0], 1.05 * im_shape[0]])
    f.savefig('/tmp/{0}.png'.format(root))

    # Write the catalog to local disk
    catalog.write('/tmp/{0}.catalog.fits'.format(root), format='fits')

    # Write out to S3
    s3 = boto3.resource('s3')
    s3.meta.client.upload_file('/tmp/{0}.catalog.fits'.format(root),
                               event['s3_output_bucket'],
                               '{0}/{1}.catalog.fits'.format(root, root))
    s3.meta.client.upload_file('/tmp/{0}.png'.format(root),
                               event['s3_output_bucket'],
                               'PNG/{0}.png'.format(root))
Example #32
0
    def find_stars(self, image: Image) -> Table:
        """Find stars in given image and append catalog.

        Args:
            image: Image to find stars in.

        Returns:
            Full table with results.
        """
        import sep

        # get data and make it continuous
        data = image.data.copy()

        # mask?
        mask = image.mask.data if image.mask is not None else None

        # estimate background, probably we need to byte swap, and subtract it
        try:
            bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
        except ValueError as e:
            data = data.byteswap(True).newbyteorder()
            bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
        bkg.subfrom(data)

        # extract sources
        try:
            sources = sep.extract(data, self.threshold, err=bkg.globalrms, minarea=self.minarea,
                                  deblend_nthresh=self.deblend_nthresh, deblend_cont=self.deblend_cont,
                                  clean=self.clean, clean_param=self.clean_param, mask=mask)
        except:
            log.exception('An error has occured.')
            return Table()

        # convert to astropy table
        sources = Table(sources)

        # only keep sources with detection flag < 8
        sources = sources[sources['flag'] < 8]

        # Calculate the ellipticity
        sources['ellipticity'] = 1.0 - (sources['b'] / sources['a'])

        # calculate the FWHMs of the stars
        fwhm = 2.0 * (np.log(2) * (sources['a'] ** 2.0 + sources['b'] ** 2.0)) ** 0.5
        sources['fwhm'] = fwhm

        # get gain
        gain = image.header['DET-GAIN'] if 'DET-GAIN' in image.header else None

        # Kron radius
        kronrad, krflag = sep.kron_radius(data, sources['x'], sources['y'], sources['a'], sources['b'],
                                          sources['theta'], 6.0)
        sources['flag'] |= krflag
        sources['kronrad'] = kronrad

        # equivalent of FLUX_AUTO
        flux, fluxerr, flag = sep.sum_ellipse(data, sources['x'], sources['y'], sources['a'], sources['b'],
                                              sources['theta'], 2.5 * kronrad, subpix=1, mask=mask,
                                              err=bkg.rms(), gain=gain)
        sources['flag'] |= flag
        sources['flux'] = flux
        sources['fluxerr'] = fluxerr

        # radii at 0.25, 0.5, and 0.75 flux
        flux_radii, flag = sep.flux_radius(data, sources['x'], sources['y'], 6.0 * sources['a'], [0.25, 0.5, 0.75],
                                           normflux=sources['flux'], subpix=5)
        sources['flag'] |= flag
        sources['fluxrad25'] = flux_radii[:, 0]
        sources['fluxrad50'] = flux_radii[:, 1]
        sources['fluxrad75'] = flux_radii[:, 2]

        # xwin/ywin
        sig = 2. / 2.35 * sources['fluxrad50']
        xwin, ywin, flag = sep.winpos(data, sources['x'], sources['y'], sig)
        sources['flag'] |= flag
        sources['xwin'] = xwin
        sources['ywin'] = ywin

        # perform aperture photometry for diameters of 1" to 8"
        for diameter in [1, 2, 3, 4, 5, 6, 7, 8]:
            flux, fluxerr, flag = sep.sum_circle(data, sources['x'], sources['y'],
                                                 diameter / 2. / image.pixel_scale,
                                                 mask=mask, err=bkg.rms(), gain=gain)
            sources['fluxaper{0}'.format(diameter)] = flux
            sources['fluxerr{0}'.format(diameter)] = fluxerr
            sources['flag'] |= flag

        # average background at each source
        # since SEP sums up whole pixels, we need to do the same on an image of ones for the background_area
        bkgflux, fluxerr, flag = sep.sum_ellipse(bkg.back(), sources['x'], sources['y'],
                                                 sources['a'], sources['b'], np.pi / 2.0,
                                                 2.5 * sources['kronrad'], subpix=1)
        background_area, _, _ = sep.sum_ellipse(np.ones(shape=bkg.back().shape), sources['x'], sources['y'],
                                                sources['a'], sources['b'], np.pi / 2.0,
                                                2.5 * sources['kronrad'], subpix=1)
        sources['background'] = bkgflux
        sources['background'][background_area > 0] /= background_area[background_area > 0]

        # match fits conventions
        sources['x'] += 1.0
        sources['xpeak'] += 1
        sources['xwin'] += 1.0
        sources['xmin'] += 1
        sources['xmax'] += 1
        sources['y'] += 1.0
        sources['ypeak'] += 1
        sources['ywin'] += 1.0
        sources['ymin'] += 1
        sources['ymax'] += 1
        sources['theta'] = np.degrees(sources['theta'])

        # pick columns for catalog
        cat = sources['x', 'y', 'xwin', 'ywin', 'xpeak', 'ypeak',
                      'flux', 'fluxerr', 'peak', 'fluxaper1', 'fluxerr1',
                      'fluxaper2', 'fluxerr2', 'fluxaper3', 'fluxerr3',
                      'fluxaper4', 'fluxerr4', 'fluxaper5', 'fluxerr5',
                      'fluxaper6', 'fluxerr6', 'fluxaper7', 'fluxerr7',
                      'fluxaper8', 'fluxerr8', 'background', 'fwhm',
                      'a', 'b', 'theta', 'kronrad', 'ellipticity',
                      'fluxrad25', 'fluxrad50', 'fluxrad75',
                      'x2', 'y2', 'xy', 'flag']

        # set it
        image.catalog = cat

        # return full catalog
        return sources
Example #33
0
    def AutoPhot(self, Kron_fact=2.5, min_diameter=3.5, write=True):
        kronrad, krflag = sep.kron_radius(self.dat, self.src['x'],
                                          self.src['y'], self.src['a'],
                                          self.src['b'], self.src['theta'], 6.)
        kronrad[np.isnan(kronrad) == True] = 0.
        flux, fluxerr, flag = sep.sum_ellipse(self.dat,
                                              self.src['x'],
                                              self.src['y'],
                                              self.src['a'],
                                              self.src['b'],
                                              self.src['theta'],
                                              Kron_fact * kronrad,
                                              err=self.skysigma,
                                              gain=self.gain,
                                              subpix=0)
        flag |= krflag  # Combining flags
        r_min = 0.5 * min_diameter

        use_circle = kronrad * np.sqrt(self.src['a'] * self.src['b']) < r_min
        cflux, cfluxerr, cflag = sep.sum_circle(self.dat,
                                                self.src['x'][use_circle],
                                                self.src['y'][use_circle],
                                                r_min,
                                                err=self.skysigma,
                                                gain=self.gain,
                                                subpix=0)
        flux[use_circle] = cflux
        fluxerr[use_circle] = cfluxerr
        flag[use_circle] = cflag

        mag = self.zmag - 2.5 * np.log10(flux)
        magerr = (2.5 / np.log(10.0)) * (fluxerr / flux)

        r, flag = sep.flux_radius(self.dat,
                                  self.src['x'],
                                  self.src['y'],
                                  6.0 * self.src['a'],
                                  0.5,
                                  normflux=flux,
                                  subpix=5)

        ra, dec = self.wcs.all_pix2world(self.src['x'] + 1, self.src['y'] + 1,
                                         1)

        df = pd.DataFrame(
            data={
                'x': self.src['x'],
                'y': self.src['y'],
                'ra': ra,
                'dec': dec,
                'a': self.src['a'],
                'b': self.src['b'],
                'theta': self.src['theta'],
                'flux': flux,
                'e_flux': fluxerr,
                'mag': mag,
                'e_mag': magerr,
                'kronrad': kronrad,
                'flxrad': r,
                'flag': flag
            })

        if write:
            df.to_csv(ip.tmp_dir + 'auto_' + self.img.split('.fits')[0] +
                      '.csv')

            f = open(ip.tmp_dir + 'src_' + self.img.split('.fits')[0] + '.reg',
                     'w')
            for i in np.arange(self.nsrc):
                f.write('{0:.3f}  {1:.3f}\n'.format(self.src['x'][i] + 1,
                                                    self.src['y'][i] + 1))
            f.close()

        return df
Example #34
0
def test_masked_segmentation_measurements():
    """Test measurements with segmentation masking"""

    NX = 100
    data = np.zeros((NX * 2, NX * 2))
    yp, xp = np.indices(data.shape)

    ####
    # Make two 2D gaussians that slightly overlap

    # width of the 2D objects
    gsigma = 10.

    # offset between two gaussians in sigmas
    off = 4

    for xy in [[NX, NX], [NX + off * gsigma, NX + off * gsigma]]:
        R = np.sqrt((xp - xy[0])**2 + (yp - xy[1])**2)
        g_i = np.exp(-R**2 / 2 / gsigma**2)
        data += g_i

    # Absolute total
    total_exact = g_i.sum()

    # Add some noise
    rms = 0.02
    np.random.seed(1)
    data += np.random.normal(size=data.shape) * rms

    # Run source detection
    objs, segmap = sep.extract(data,
                               thresh=1.2,
                               err=rms,
                               mask=None,
                               segmentation_map=True)

    seg_id = np.arange(1, len(objs) + 1, dtype=np.int32)

    # Compute Kron/Auto parameters
    x, y, a, b = objs['x'], objs['y'], objs['a'], objs['b']
    theta = objs['theta']

    kronrad, krflag = sep.kron_radius(data, x, y, a, b, theta, 6.0)

    flux_auto, fluxerr, flag = sep.sum_ellipse(data,
                                               x,
                                               y,
                                               a,
                                               b,
                                               theta,
                                               2.5 * kronrad,
                                               segmap=segmap,
                                               seg_id=seg_id,
                                               subpix=1)

    # Test total flux
    assert_allclose(flux_auto, total_exact, rtol=5.e-2)

    # Flux_radius
    for flux_fraction in [0.2, 0.5]:

        # Exact solution
        rhalf_exact = np.sqrt(-np.log(1 - flux_fraction) * gsigma**2 * 2)

        # Masked measurement
        flux_radius, flag = sep.flux_radius(data,
                                            x,
                                            y,
                                            6. * a,
                                            flux_fraction,
                                            seg_id=seg_id,
                                            segmap=segmap,
                                            normflux=flux_auto,
                                            subpix=5)

        # Test flux fraction
        assert_allclose(flux_radius, rhalf_exact, rtol=5.e-2)

    if False:
        print('test_masked_flux_radius')
        print(total_exact, flux_auto)
        print(rhalf_exact, flux_radius)
Example #35
0
    def _measure(self, img, sources, mask=None):

        logger.info('measuring source parameters')

        # HACK: issues with numerical precision
        # must have pi/2 <= theta <= npi/2
        sources[np.abs(np.abs(sources['theta']) -
                       np.pi / 2) < 1e-6] = np.pi / 2

        for p in ['x', 'y', 'a', 'b', 'theta']:
            sources = sources[~np.isnan(sources[p])]

        # calculate "AUTO" parameters
        kronrad, krflag = sep.kron_radius(img,
                                          sources['x'],
                                          sources['y'],
                                          sources['a'],
                                          sources['b'],
                                          sources['theta'],
                                          6.0,
                                          mask=mask)

        flux, fluxerr, flag = sep.sum_ellipse(img,
                                              sources['x'],
                                              sources['y'],
                                              sources['a'],
                                              sources['b'],
                                              sources['theta'],
                                              2.5 * kronrad,
                                              subpix=5,
                                              mask=mask)
        flag |= krflag  # combine flags into 'flag'

        sources = sources[~np.isnan(flux)]
        flux = flux[~np.isnan(flux)]
        sources = sources[flux > 0]
        flux = flux[flux > 0]

        mag_auto = self.zpt - 2.5 * np.log10(flux)
        r, flag = sep.flux_radius(img,
                                  sources['x'],
                                  sources['y'],
                                  6. * sources['a'],
                                  0.5,
                                  normflux=flux,
                                  subpix=5,
                                  mask=mask)

        sources['mag_auto'] = mag_auto
        sources['flux_auto'] = flux
        sources['flux_radius'] = r * self.pixscale

        # approximate fwhm
        r_squared = sources['a']**2 + sources['b']**2
        sources['fwhm'] = 2 * np.sqrt(np.log(2) * r_squared) * self.pixscale

        q = sources['b'] / sources['a']
        area = np.pi * q * sources['flux_radius']**2
        sources['mu_ave_auto'] = sources['mag_auto'] + 2.5 * np.log10(2 * area)

        area_arcsec = np.pi * (self.psf_fwhm / 2)**2 * self.pixscale**2
        flux, fluxerr, flag = sep.sum_circle(img,
                                             sources['x'],
                                             sources['y'],
                                             self.psf_fwhm / 2,
                                             subpix=5,
                                             mask=mask)
        flux[flux <= 0] = np.nan
        mu_0 = self.zpt - 2.5 * np.log10(flux / area_arcsec)

        sources['mu_0_aper'] = mu_0

        return sources
Example #36
0
def sourcephot(catalogue,image,segmap,detection,instrument='MUSE',dxp=0.,dyp=0.,
               noise=[False],zpab=False, kn=2.5, circap=1.0):

    """ 

    Get a source catalogue from findsources and a fits image with ZP
    and compute magnitudes in that filter 

    catalogue -> source cat from findsources
    image     -> fits image with ZP in header
    segmap    -> fits of segmentation map 
    detection -> the detection image, used to compute Kron radius 

    instrument -> if not MUSE, map positions from detection to image

    dxp,dyp    -> shifts in pixel of image to register MUSE and image astrometry   
   
    noise      -> if set to a noise model, use equation noise[0]*noise[1]*npix**noise[2]
                  to compute the error

    zpab  -> if ZPAB (zeropoint AB) not stored in header, must be supplied

    kn   -> factor to be used when scaling Kron apertures [sextractor default 2.5]
  
    circap -> radius in arcsec for aperture photmetry to be used when Kron aperture fails 

    """  

    from astropy.io import fits
    import numpy as np
    import sep
    import matplotlib.pyplot as plt
    from astropy.table import Table
    from astropy import wcs 


    #grab root name 
    rname=((image.split('/')[-1]).split('.fits'))[0]
    print ('Working on {}'.format(rname))

    #open the catalogue/fits 
    cat=fits.open(catalogue)
    img=fits.open(image)
    seg=fits.open(segmap)
    det=fits.open(detection)

    #grab reference wcs from detection image 
    try:
        wref=wcs.WCS(det[1].header)
    except:
        wref = wcs.WCS(det[0].header)
    psref=wref.pixel_scale_matrix[1,1]*3600.
    print ('Reference pixel size {}'.format(psref))


    #if not handling MUSE, special cases for format of data
    if('MUSE' not in instrument):
        #handle instrument cases
        if('LRIS' in instrument):
            #data 
            imgdata=img[1].data
            #place holder for varaince as will use noise model below
            vardata=imgdata*0+1
            vardata=vardata.byteswap(True).newbyteorder()
            #grab wcs image
            wimg=wcs.WCS(img[1].header)
            psimg=wimg.pixel_scale_matrix[1,1]*3600.
            #store the ZP 
            if(zpab):
                img[0].header['ZPAB']=zpab
        else:
            print('Instrument not supported!!')
            exit()
    else:
        #for muse, keep eveything the same
        imgdata=img[0].data
        vardata=img[1].data
        psimg=psref

    #grab flux and var
    dataflx=np.nan_to_num(imgdata.byteswap(True).newbyteorder())
    datavar=np.nan_to_num(vardata.byteswap(True).newbyteorder())
    # import pdb; pdb.set_trace()
    #grab detection and seg mask
    try:
        detflx=np.nan_to_num(det[1].data.byteswap(True).newbyteorder())
    except:
        detflx = np.nan_to_num(det[0].data.byteswap(True).newbyteorder())

    #go back to 1d
    if(len(seg[0].data.shape)>2):
        segmask=(np.nan_to_num(seg[0].data.byteswap(True).newbyteorder()))[0,:,:]
    else:
        segmask=(np.nan_to_num(seg[0].data.byteswap(True).newbyteorder()))


    #if needed, map the segmap to new image with transformation
    if('MUSE' not in instrument):
        #allocate space for transformed segmentation map
        segmasktrans=np.zeros(dataflx.shape)
        print("Remapping segmentation map to new image...")

        #loop over original segmap and map to trasformed one
        #Just use nearest pixel, and keep only 1 when multiple choices 
        for xx in range(segmask.shape[0]):
            for yy in range(segmask.shape[1]):
                #go to world
                radec=wref.wcs_pix2world([[yy,xx]],0)
                #back to new instrument pixel 
                newxy=wimg.wcs_world2pix(radec,0)
                #apply shift to register WCS
                newxy[0][1]=newxy[0][1]+dyp
                newxy[0][0]=newxy[0][0]+dxp
                segmasktrans[newxy[0][1],newxy[0][0]]=segmask[xx,yy]
                
                #grow buffer as needed by individual instruments
                #This accounts for resampling to finer pixel size
                if('LRIS' in instrument):
                    segmasktrans[newxy[0][1]+1,newxy[0][0]+1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]+1,newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]+1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]+1,newxy[0][0]]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]]=segmask[xx,yy]
                    segmasktrans[newxy[0][1],newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1],newxy[0][0]+1]=segmask[xx,yy]
                 
        #dump the transformed segmap for checking 
        hdumain  = fits.PrimaryHDU(segmasktrans,header=img[1].header)
        hdulist = fits.HDUList(hdumain)
        hdulist.writeto("{}_segremap.fits".format(rname),clobber=True)
    else:
        #no transformation needed
        segmasktrans=segmask

    #source to extract
    nsrc=len(cat[1].data)
    print('Extract photometry for {} sources'.format(nsrc))
    phot = Table(names=('ID', 'MAGAP', 'MAGAP_ERR','FXAP', 'FXAP_ERR', 
                        'RAD', 'MAGSEG', 'MAGSEG_ERR', 'FXSEG', 'FXSEG_ERR','ZP'), 
                 dtype=('i4','f4','f4','f4','f4','f4','f4','f4','f4','f4','f4'))
    
   
    #create check aperture mask 
    checkaperture=np.zeros(dataflx.shape)
    print('Computing photometry for objects...')

    #loop over each source
    for idobj in range(nsrc):
        
        #########
        #Find positions etc and transform as appropriate
        #########
                
        #extract MUSE source paramaters 
        x= cat[1].data['x'][idobj]
        y= cat[1].data['y'][idobj]
        a= cat[1].data['a'][idobj]
        b= cat[1].data['b'][idobj]
        theta= cat[1].data['theta'][idobj]

        #compute kron radius on MUSE detection image 
        #Kron rad in units of a,b
        tmpdata=np.copy(detflx)
        tmpmask=np.copy(segmask)
        #mask all other sources to avoid overlaps but keep desired one
        pixels=np.where(tmpmask == idobj+1) 
        tmpmask[pixels]=0

        #compute kron radius [pixel of reference image]
        kronrad, flg = sep.kron_radius(tmpdata,x,y,a,b,theta,6.0,mask=tmpmask)

        #plt.imshow(np.log10(tmpdata+1),origin='low')
        #plt.show()
        #exit()

        #now check if size is sensible in units of MUSE data 
        rmin = 2.0  #MUSE pix 
        use_circle = kronrad * np.sqrt(a*b) < rmin
      
        #use circular aperture of 2" in muse pixel unit
        rcircap = circap/psref
        
        #now use info to compute photometry and apply 
        #spatial transformation if needed
        if('MUSE' not in instrument):
            #map centre of aperture - +1 reference
            #go to world
            radec=wref.wcs_pix2world([[x,y]],1)
            #back to new instrument pixel 
            newxy=wimg.wcs_world2pix(radec,1)
            #apply shift to register WCS
            xphot=newxy[0][0]+dxp
            yphot=newxy[0][1]+dyp
                      
            #scale radii to new pixel size 
            rminphot=rcircap*psref/psimg
            aphot=a*psref/psimg
            bphot=b*psref/psimg
            #Kron radius in units of a,b

        else:
            #for muse, transfer to same units
            xphot=x
            yphot=y
            rminphot=rcircap
            aphot=a
            bphot=b     
            
        #####
        #Compute local sky 
        #####
        skyreg=kn*kronrad*np.sqrt(aphot*bphot)+15
        if (yphot-skyreg < 0.0): yphot=skyreg
        if (xphot-skyreg < 0.0): xphot=skyreg
        if (yphot+skyreg > segmasktrans.shape[0]-1): yphot=segmasktrans.shape[0]-1-skyreg
        if (xphot+skyreg > segmasktrans.shape[1]-1): xphot=segmasktrans.shape[1]-1-skyreg
        #print(int(yphot-skyreg),int(yphot+skyreg),int(xphot-skyreg),int(xphot+skyreg))
        cutskymask=segmasktrans[int(yphot-skyreg):int(yphot+skyreg),int(xphot-skyreg):int(xphot+skyreg)]
        cutskydata=dataflx[int(yphot-skyreg):int(yphot+skyreg),int(xphot-skyreg):int(xphot+skyreg)]
        skymedian=np.nan_to_num(np.median(cutskydata[np.where(cutskymask < 1.0)]))

        #print skymedian    

        #plt.imshow(cutskymask,origin='low')
        #plt.show()
        #if(idobj > 30):
        #    exit()


        #########
        #Now grab the Kron mag computed using detection image
        #########
   
        #mask all other objects to avoid blending   
        tmpdata=np.copy(dataflx)
        #apply local sky subtraction 
        tmpdata=tmpdata-skymedian
        tmpvar=np.copy(datavar)
        tmpmask=np.copy(segmasktrans)
        pixels=np.where(tmpmask == idobj+1) 
        tmpmask[pixels]=0

        #plt.imshow(tmpmask,origin='low')
        #plt.show()
        #exit()

        #circular aperture
        if(use_circle):        
           
            #flux in circular aperture
            flux_kron, err, flg = sep.sum_circle(tmpdata,xphot,yphot,rminphot,mask=tmpmask)
            #propagate variance
            fluxvar, err, flg = sep.sum_circle(tmpvar,xphot,yphot,rminphot,mask=tmpmask)
            #store Rused in arcsec
            rused=rminphot*psimg

            #update check aperture
            tmpcheckaper=np.zeros(dataflx.shape,dtype=bool)
            sep.mask_ellipse(tmpcheckaper,xphot,yphot,1.,1.,0.,r=rminphot)
            checkaperture=checkaperture+tmpcheckaper*(idobj+1)

        #kron apertures 
        else:
            #kron flux 
            flux_kron, err, flg = sep.sum_ellipse(tmpdata,xphot, yphot, aphot, bphot, theta, kn*kronrad,
                                                  mask=tmpmask)            
            #propagate variance 
            fluxvar, err, flg = sep.sum_ellipse(tmpvar,xphot,yphot, aphot, bphot, theta, kn*kronrad,
                                                mask=tmpmask)
            #translate in radius
            rused=kn*kronrad*psimg*np.sqrt(aphot*bphot)

            #update check aperture
            tmpcheckaper=np.zeros(dataflx.shape,dtype=bool)
            sep.mask_ellipse(tmpcheckaper,xphot,yphot,aphot,bphot,theta,r=kn*kronrad)
            checkaperture=checkaperture+tmpcheckaper*(idobj+1)

        #compute error for aperture
        if(noise[0]):
            #use model 
            appix=np.where(tmpcheckaper > 0)
            errflux_kron=noise[0]*noise[1]*len(appix[0])**noise[2]
        else:
            #propagate variance 
            errflux_kron=np.sqrt(fluxvar)

        #go to mag 
        if(flux_kron > 0):
            mag_aper=-2.5*np.log10(flux_kron)+img[0].header['ZPAB']
            errmg_aper=2.5*np.log10(1.+errflux_kron/flux_kron)
        else:
            mag_aper=99.0
            errmg_aper=99.0
        
        #find out if non detections
        if(errflux_kron >= flux_kron):
            errmg_aper=9
            mag_aper=-2.5*np.log10(2.*errflux_kron)+img[0].header['ZPAB']
          
        #######
        #grab the photometry in the segmentation map 
        #####

        #This may not work well for other instruments 
        #if images are not well aligned
        pixels=np.where(segmasktrans == idobj+1) 
        #add flux in pixels
        tmpdata=np.copy(dataflx)
        #apply sky sub
        tmpdata=tmpdata-skymedian
        flux_seg=np.sum(tmpdata[pixels])
        
        #compute noise from model or adding variance 
        if(noise[0]):
            #from model 
            errfx_seg=noise[0]*noise[1]*len(pixels[0])**noise[2]
        else:
            #add variance in pixels to compute error
            errfx_seg=np.sqrt(np.sum(datavar[pixels]))
  
        #go to mag with calibrations 
        if(flux_seg > 0):
            mag_seg=-2.5*np.log10(flux_seg)+img[0].header['ZPAB']
            errmg_seg=2.5*np.log10(1.+errfx_seg/flux_seg)     
        else:
            mag_seg=99.0
            errmg_seg=99.0
      
        #find out if non detections
        if(errfx_seg >= flux_seg):
            errmg_seg=9
            mag_seg=-2.5*np.log10(2.*errfx_seg)+img[0].header['ZPAB']
        
        #stash by id
        phot.add_row((idobj+1,mag_aper,errmg_aper,flux_kron,errflux_kron,rused,mag_seg,errmg_seg,
                      flux_seg,errfx_seg,img[0].header['ZPAB']))

    #dump the aperture check image 
    hdumain  = fits.PrimaryHDU(checkaperture,header=img[1].header)
    hdulist = fits.HDUList(hdumain)
    hdulist.writeto("{}_aper.fits".format(rname),clobber=True)

    #close
    cat.close()
    img.close()
    seg.close()
    det.close()

    return phot
Example #37
0
File: test.py Project: cmccully/sep
def test_vs_sextractor():
    """Test behavior of sep versus sextractor.

    Note: we turn deblending off for this test. This is because the
    deblending algorithm uses a random number generator. Since the sequence
    of random numbers is not the same between sextractor and sep or between
    different platforms, object member pixels (and even the number of objects)
    can differ when deblending is on.

    Deblending is turned off by setting DEBLEND_MINCONT=1.0 in the sextractor
    configuration file and by setting deblend_cont=1.0 in sep.extract().
    """

    data = np.copy(image_data)  # make an explicit copy so we can 'subfrom'
    bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3)

    # Test that SExtractor background is same as SEP:
    bkgarr = bkg.back(dtype=np.float32)
    assert_allclose(bkgarr, image_refback, rtol=1.e-5)

    # Extract objects (use deblend_cont=1.0 to disable deblending).
    bkg.subfrom(data)
    objs = sep.extract(data, 1.5*bkg.globalrms, deblend_cont=1.0)
    objs = np.sort(objs, order=['y'])

    # Read SExtractor result
    refobjs = np.loadtxt(IMAGECAT_FNAME, dtype=IMAGECAT_DTYPE)
    refobjs = np.sort(refobjs, order=['y'])

    # Found correct number of sources at the right locations?
    assert_allclose(objs['x'], refobjs['x'] - 1., atol=1.e-3)
    assert_allclose(objs['y'], refobjs['y'] - 1., atol=1.e-3)

    # Test aperture flux
    flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 5.,
                                         err=bkg.globalrms)
    assert_allclose(flux, refobjs['flux_aper'], rtol=2.e-4)
    assert_allclose(fluxerr, refobjs['fluxerr_aper'], rtol=1.0e-5)

    # check if the flags work at all (comparison values 
    assert ((flag & sep.APER_TRUNC) != 0).sum() == 4
    assert ((flag & sep.APER_HASMASKED) != 0).sum() == 0

    # Test "flux_auto"
    kr, flag = sep.kron_radius(data, objs['x'], objs['y'], objs['a'],
                               objs['b'], objs['theta'], 6.0)

    flux, fluxerr, flag = sep.sum_ellipse(data, objs['x'], objs['y'],
                                          objs['a'], objs['b'],
                                          objs['theta'], r=2.5 * kr,
                                          err=bkg.globalrms, subpix=1)

    # For some reason, one object doesn't match. It's very small
    # and kron_radius is set to 0.0 in SExtractor, but 0.08 in sep.
    # Could be due to a change in SExtractor between v2.8.6 (used to
    # generate "truth" catalog) and v2.18.11 (from which sep was forked).
    i = 56  # index is 59 when deblending is on.
    kr[i] = 0.0
    flux[i] = 0.0
    fluxerr[i] = 0.0

    # We use atol for radius because it is reported to nearest 0.01 in
    # reference objects.
    assert_allclose(2.5*kr, refobjs['kron_radius'], atol=0.01, rtol=0.) 
    assert_allclose(flux, refobjs['flux_auto'], rtol=0.0005)
    assert_allclose(fluxerr, refobjs['fluxerr_auto'], rtol=0.0005)

    # Test ellipse representation conversion
    cxx, cyy, cxy = sep.ellipse_coeffs(objs['a'], objs['b'], objs['theta'])
    assert_allclose(cxx, objs['cxx'], rtol=1.e-4)
    assert_allclose(cyy, objs['cyy'], rtol=1.e-4)
    assert_allclose(cxy, objs['cxy'], rtol=1.e-4)

    a, b, theta = sep.ellipse_axes(objs['cxx'], objs['cyy'], objs['cxy'])
    assert_allclose(a, objs['a'], rtol=1.e-4)
    assert_allclose(b, objs['b'], rtol=1.e-4)
    assert_allclose(theta, objs['theta'], rtol=1.e-4)

    #test round trip
    cxx, cyy, cxy = sep.ellipse_coeffs(a, b, theta)
    assert_allclose(cxx, objs['cxx'], rtol=1.e-4)
    assert_allclose(cyy, objs['cyy'], rtol=1.e-4)
    assert_allclose(cxy, objs['cxy'], rtol=1.e-4)

    # test flux_radius
    fr, flags = sep.flux_radius(data, objs['x'], objs['y'], 6.*refobjs['a'],
                                [0.1, 0.5, 0.6], normflux=refobjs['flux_auto'],
                                subpix=5)
    assert_allclose(fr, refobjs["flux_radius"], rtol=0.04, atol=0.01)

    # test winpos
    sig = 2. / 2.35 * fr[:, 1]  # flux_radius = 0.5
    xwin, ywin, flag = sep.winpos(data, objs['x'], objs['y'], sig)
    assert_allclose(xwin, refobjs["xwin"] - 1., rtol=0., atol=0.0025)
    assert_allclose(ywin, refobjs["ywin"] - 1., rtol=0., atol=0.0025)
Example #38
0
def sourcephot(catalogue,image,segmap,detection,instrument='MUSE',dxp=0.,dyp=0.,
               noise=[False],zpab=False, kn=2.5, circap=1.0):

    """ 

    Get a source catalogue from findsources and a fits image with ZP
    and compute magnitudes in that filter 

    catalogue -> source cat from findsources
    image     -> fits image with ZP in header
    segmap    -> fits of segmentation map 
    detection -> the detection image, used to compute Kron radius 

    instrument -> if not MUSE, map positions from detection to image

    dxp,dyp    -> shifts in pixel of image to register MUSE and image astrometry   
   
    noise      -> if set to a noise model, use equation noise[0]*noise[1]*npix**noise[2]
                  to compute the error

    zpab  -> if ZPAB (zeropoint AB) not stored in header, must be supplied

    kn   -> factor to be used when scaling Kron apertures [sextractor default 2.5]
  
    circap -> radius in arcsec for aperture photmetry to be used when Kron aperture fails 

    """  

    from astropy.io import fits
    import numpy as np
    import sep
    import matplotlib.pyplot as plt
    from astropy.table import Table
    from astropy import wcs 


    #grab root name 
    rname=((image.split('/')[-1]).split('.fits'))[0]
    print ('Working on {}'.format(rname))

    #open the catalogue/fits 
    cat=fits.open(catalogue)
    img=fits.open(image)
    seg=fits.open(segmap)
    det=fits.open(detection)

    #grab reference wcs from detection image 
    wref=wcs.WCS(det[0].header)
    psref=wref.pixel_scale_matrix[1,1]*3600.
    print ('Reference pixel size {}'.format(psref))


    #if not handling MUSE, special cases for format of data
    if('MUSE' not in instrument):
        #handle instrument cases
        if('LRIS' in instrument):
            #data 
            imgdata=img[1].data
            #place holder for varaince as will use noise model below
            vardata=imgdata*0+1
            vardata=vardata.byteswap(True).newbyteorder()
            #grab wcs image
            wimg=wcs.WCS(img[1].header)
            psimg=wimg.pixel_scale_matrix[1,1]*3600.
            #store the ZP 
            if(zpab):
                img[0].header['ZPAB']=zpab
        else:
            print 'Instrument not supported!!'
            exit()
    else:
        #for muse, keep eveything the same
        imgdata=img[0].data
        vardata=img[1].data
        psimg=psref

    #grab flux and var
    dataflx=np.nan_to_num(imgdata.byteswap(True).newbyteorder())
    datavar=np.nan_to_num(vardata.byteswap(True).newbyteorder())
    #grab detection and seg mask 
    detflx=np.nan_to_num(det[0].data.byteswap(True).newbyteorder())
    #go back to 1d
    segmask=(np.nan_to_num(seg[0].data.byteswap(True).newbyteorder()))[0,:,:]

    #if needed, map the segmap to new image with transformation
    if('MUSE' not in instrument):
        #allocate space for transformed segmentation map
        segmasktrans=np.zeros(dataflx.shape)
        print "Remapping segmentation map to new image..."

        #loop over original segmap and map to trasformed one
        #Just use nearest pixel, and keep only 1 when multiple choices 
        for xx in range(segmask.shape[0]):
            for yy in range(segmask.shape[1]):
                #go to world
                radec=wref.wcs_pix2world([[yy,xx]],0)
                #back to new instrument pixel 
                newxy=wimg.wcs_world2pix(radec,0)
                #apply shift to register WCS
                newxy[0][1]=newxy[0][1]+dyp
                newxy[0][0]=newxy[0][0]+dxp
                segmasktrans[newxy[0][1],newxy[0][0]]=segmask[xx,yy]
                
                #grow buffer as needed by individual instruments
                #This accounts for resampling to finer pixel size
                if('LRIS' in instrument):
                    segmasktrans[newxy[0][1]+1,newxy[0][0]+1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]+1,newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]+1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]+1,newxy[0][0]]=segmask[xx,yy]
                    segmasktrans[newxy[0][1]-1,newxy[0][0]]=segmask[xx,yy]
                    segmasktrans[newxy[0][1],newxy[0][0]-1]=segmask[xx,yy]
                    segmasktrans[newxy[0][1],newxy[0][0]+1]=segmask[xx,yy]
                 
        #dump the transformed segmap for checking 
        hdumain  = fits.PrimaryHDU(segmasktrans,header=img[1].header)
        hdulist = fits.HDUList(hdumain)
        hdulist.writeto("{}_segremap.fits".format(rname),clobber=True)
    else:
        #no transformation needed
        segmasktrans=segmask

    #source to extract
    nsrc=len(cat[1].data)
    print('Extract photometry for {} sources'.format(nsrc))
    phot = Table(names=('ID', 'MAGAP', 'MAGAP_ERR','FXAP', 'FXAP_ERR', 
                        'RAD', 'MAGSEG', 'MAGSEG_ERR', 'FXSEG', 'FXSEG_ERR','ZP'), 
                 dtype=('i4','f4','f4','f4','f4','f4','f4','f4','f4','f4','f4'))
    
   
    #create check aperture mask 
    checkaperture=np.zeros(dataflx.shape)
    print('Computing photometry for objects...')

    #loop over each source
    for idobj in range(nsrc):
        
        #########
        #Find positions etc and transform as appropriate
        #########
                
        #extract MUSE source paramaters 
        x= cat[1].data['x'][idobj]
        y= cat[1].data['y'][idobj]
        a= cat[1].data['a'][idobj]
        b= cat[1].data['b'][idobj]
        theta= cat[1].data['theta'][idobj]

        #compute kron radius on MUSE detection image 
        #Kron rad in units of a,b
        tmpdata=np.copy(detflx)
        tmpmask=np.copy(segmask)
        #mask all other sources to avoid overlaps but keep desired one
        pixels=np.where(tmpmask == idobj+1) 
        tmpmask[pixels]=0

        #compute kron radius [pixel of reference image]
        kronrad, flg = sep.kron_radius(tmpdata,x,y,a,b,theta,6.0,mask=tmpmask)

        #plt.imshow(np.log10(tmpdata+1),origin='low')
        #plt.show()
        #exit()

        #now check if size is sensible in units of MUSE data 
        rmin = 2.0  #MUSE pix 
        use_circle = kronrad * np.sqrt(a*b) < rmin
      
        #use circular aperture of 2" in muse pixel unit
        rcircap = circap/psref
        
        #now use info to compute photometry and apply 
        #spatial transformation if needed
        if('MUSE' not in instrument):
            #map centre of aperture - +1 reference
            #go to world
            radec=wref.wcs_pix2world([[x,y]],1)
            #back to new instrument pixel 
            newxy=wimg.wcs_world2pix(radec,1)
            #apply shift to register WCS
            xphot=newxy[0][0]+dxp
            yphot=newxy[0][1]+dyp
                      
            #scale radii to new pixel size 
            rminphot=rcircap*psref/psimg
            aphot=a*psref/psimg
            bphot=b*psref/psimg
            #Kron radius in units of a,b

        else:
            #for muse, transfer to same units
            xphot=x
            yphot=y
            rminphot=rcircap
            aphot=a
            bphot=b     
            
        #####
        #Compute local sky 
        #####
        skyreg=kn*kronrad*np.sqrt(aphot*bphot)+15
        cutskymask=segmasktrans[yphot-skyreg:yphot+skyreg,xphot-skyreg:xphot+skyreg]
        cutskydata=dataflx[yphot-skyreg:yphot+skyreg,xphot-skyreg:xphot+skyreg]
        skymedian=np.nan_to_num(np.median(cutskydata[np.where(cutskymask < 1.0)]))

        #print skymedian    
        #plt.imshow(cutskymask,origin='low')
        #plt.show()
        #if(idobj > 30):
        #    exit()


        #########
        #Now grab the Kron mag computed using detection image
        #########
   
        #mask all other objects to avoid blending   
        tmpdata=np.copy(dataflx)
        #apply local sky subtraction 
        tmpdata=tmpdata-skymedian
        tmpvar=np.copy(datavar)
        tmpmask=np.copy(segmasktrans)
        pixels=np.where(tmpmask == idobj+1) 
        tmpmask[pixels]=0

        #plt.imshow(tmpmask,origin='low')
        #plt.show()
        #exit()

        #circular aperture
        if(use_circle):        
           
            #flux in circular aperture
            flux_kron, err, flg = sep.sum_circle(tmpdata,xphot,yphot,rminphot,mask=tmpmask)
            #propagate variance
            fluxvar, err, flg = sep.sum_circle(tmpvar,xphot,yphot,rminphot,mask=tmpmask)
            #store Rused in arcsec
            rused=rminphot*psimg

            #update check aperture
            tmpcheckaper=np.zeros(dataflx.shape,dtype=bool)
            sep.mask_ellipse(tmpcheckaper,xphot,yphot,1.,1.,0.,r=rminphot)
            checkaperture=checkaperture+tmpcheckaper*(idobj+1)

        #kron apertures 
        else:
            #kron flux 
            flux_kron, err, flg = sep.sum_ellipse(tmpdata,xphot, yphot, aphot, bphot, theta, kn*kronrad,
                                                  mask=tmpmask)            
            #propagate variance 
            fluxvar, err, flg = sep.sum_ellipse(tmpvar,xphot,yphot, aphot, bphot, theta, kn*kronrad,
                                                mask=tmpmask)
            #translate in radius
            rused=kn*kronrad*psimg*np.sqrt(aphot*bphot)

            #update check aperture
            tmpcheckaper=np.zeros(dataflx.shape,dtype=bool)
            sep.mask_ellipse(tmpcheckaper,xphot,yphot,aphot,bphot,theta,r=kn*kronrad)
            checkaperture=checkaperture+tmpcheckaper*(idobj+1)

        #compute error for aperture
        if(noise[0]):
            #use model 
            appix=np.where(tmpcheckaper > 0)
            errflux_kron=noise[0]*noise[1]*len(appix[0])**noise[2]
        else:
            #propagate variance 
            errflux_kron=np.sqrt(fluxvar)

        #go to mag 
        if(flux_kron > 0):
            mag_aper=-2.5*np.log10(flux_kron)+img[0].header['ZPAB']
            errmg_aper=2.5*np.log10(1.+errflux_kron/flux_kron)
        else:
            mag_aper=99.0
            errmg_aper=99.0
        
        #find out if non detections
        if(errflux_kron >= flux_kron):
            errmg_aper=9
            mag_aper=-2.5*np.log10(2.*errflux_kron)+img[0].header['ZPAB']
          
        #######
        #grab the photometry in the segmentation map 
        #####

        #This may not work well for other instruments 
        #if images are not well aligned
        pixels=np.where(segmasktrans == idobj+1) 
        #add flux in pixels
        tmpdata=np.copy(dataflx)
        #apply sky sub
        tmpdata=tmpdata-skymedian
        flux_seg=np.sum(tmpdata[pixels])
        
        #compute noise from model or adding variance 
        if(noise[0]):
            #from model 
            errfx_seg=noise[0]*noise[1]*len(pixels[0])**noise[2]
        else:
            #add variance in pixels to compute error
            errfx_seg=np.sqrt(np.sum(datavar[pixels]))
  
        #go to mag with calibrations 
        if(flux_seg > 0):
            mag_seg=-2.5*np.log10(flux_seg)+img[0].header['ZPAB']
            errmg_seg=2.5*np.log10(1.+errfx_seg/flux_seg)     
        else:
            mag_seg=99.0
            errmg_seg=99.0
      
        #find out if non detections
        if(errfx_seg >= flux_seg):
            errmg_seg=9
            mag_seg=-2.5*np.log10(2.*errfx_seg)+img[0].header['ZPAB']
        
        #stash by id
        phot.add_row((idobj+1,mag_aper,errmg_aper,flux_kron,errflux_kron,rused,mag_seg,errmg_seg,
                      flux_seg,errfx_seg,img[0].header['ZPAB']))

    #dump the aperture check image 
    hdumain  = fits.PrimaryHDU(checkaperture,header=img[1].header)
    hdulist = fits.HDUList(hdumain)
    hdulist.writeto("{}_aper.fits".format(rname),clobber=True)

    #close
    cat.close()
    img.close()
    seg.close()
    det.close()

    return phot
Example #39
0
    def get_hostgalaxy_params(self, scaleup=2.5):
        '''
        Gives x, y, a, b, theta of host galaxy
        determined to be the object with smallest DLR from target
        '''
        if not hasattr(self, "hg_ellipse"):
            raise AttributeError(
                "No host galaxy detected yet. " +
                "Run self.get_hostgalaxy() or set self.hg_ellipse")
        self.var_count = {
            band: self.target.imgcutout[band].weightimage.data**(-2)
            for band in self.bands
        }

        counts_res = {
            band: sep.sum_ellipse(self.cutout[band].data,
                                  self.hg_ellipse[0],
                                  self.hg_ellipse[1],
                                  2 * self.hg_ellipse[2] * scaleup,
                                  2 * self.hg_ellipse[3] * scaleup,
                                  self.hg_ellipse[4],
                                  var=self.var_count[band])
            for band in self.bands
        }
        self.hg_count = {
            band: [counts_res[band][0], counts_res[band][1]]
            for band in self.bands
        }

        self.hg_flux = {
            band: [
                self.count_to_flux(self.hg_count[band][0]),
                self.count_to_flux(self.hg_count[band][1])
            ]
            for band in self.bands
        }

        self.var_flux = {
            band: self.hg_flux[band][-1]**2
            for band in self.bands
        }

        self.hg_mag = {
            band: np.array(
                flux_to_mag(self.hg_flux[band][0],
                            self.hg_flux[band][1],
                            zp=ps1_zp[band]))
            for band in self.bands
        }

        phtpt = {
            band: get_photopoint(self.hg_flux[band][0],
                                 self.var_flux[band],
                                 lbda=PANSTARRS_INFO[band]['lbda'],
                                 bandname=PANSTARRS_INFO[band]['band'])
            for band in ['g', 'i']
        }
        MassEstimate_obj = get_massestimator(
            photopoints=[phtpt['g'], phtpt['i']])
        MassEstimate_obj.set_target(get_target(zcmb=self.z))
        self.hg_mass = MassEstimate_obj.get_estimate()

        return self.hg_mass
Example #40
0
    def do_stage(self, images):
        for i, image in enumerate(images):
            try:
                # Set the number of source pixels to be 5% of the total. This keeps us safe from
                # satellites and airplanes.
                sep.set_extract_pixstack(int(image.nx * image.ny * 0.05))

                data = image.data.copy()
                error = (np.abs(data) + image.readnoise ** 2.0) ** 0.5
                mask = image.bpm > 0

                # Fits can be backwards byte order, so fix that if need be and subtract
                # the background
                try:
                    bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
                except ValueError:
                    data = data.byteswap(True).newbyteorder()
                    bkg = sep.Background(data, mask=mask, bw=32, bh=32, fw=3, fh=3)
                bkg.subfrom(data)

                # Do an initial source detection
                # TODO: Add back in masking after we are sure SEP works
                sources = sep.extract(data, self.threshold, minarea=self.min_area,
                                      err=error, deblend_cont=0.005)

                # Convert the detections into a table
                sources = Table(sources)

                # Calculate the ellipticity
                sources['ellipticity'] = 1.0 - (sources['b'] / sources['a'])

                # Fix any value of theta that are invalid due to floating point rounding
                # -pi / 2 < theta < pi / 2
                sources['theta'][sources['theta'] > (np.pi / 2.0)] -= np.pi
                sources['theta'][sources['theta'] < (-np.pi / 2.0)] += np.pi

                # Calculate the kron radius
                kronrad, krflag = sep.kron_radius(data, sources['x'], sources['y'],
                                                  sources['a'], sources['b'],
                                                  sources['theta'], 6.0)
                sources['flag'] |= krflag
                sources['kronrad'] = kronrad

                # Calcuate the equivilent of flux_auto
                flux, fluxerr, flag = sep.sum_ellipse(data, sources['x'], sources['y'],
                                                      sources['a'], sources['b'],
                                                      np.pi / 2.0, 2.5 * kronrad,
                                                      subpix=1, err=error)
                sources['flux'] = flux
                sources['fluxerr'] = fluxerr
                sources['flag'] |= flag

                # Calculate the FWHMs of the stars:
                fwhm = 2.0 * (np.log(2) * (sources['a'] ** 2.0 + sources['b'] ** 2.0)) ** 0.5
                sources['fwhm'] = fwhm

                # Cut individual bright pixels. Often cosmic rays
                sources = sources[fwhm > 1.0]

                # Measure the flux profile
                flux_radii, flag = sep.flux_radius(data, sources['x'], sources['y'],
                                                   6.0 * sources['a'], [0.25, 0.5, 0.75],
                                                   normflux=sources['flux'], subpix=5)
                sources['flag'] |= flag
                sources['fluxrad25'] = flux_radii[:, 0]
                sources['fluxrad50'] = flux_radii[:, 1]
                sources['fluxrad75'] = flux_radii[:, 2]

                # Calculate the windowed positions
                sig = 2.0 / 2.35 * sources['fluxrad50']
                xwin, ywin, flag = sep.winpos(data, sources['x'], sources['y'], sig)
                sources['flag'] |= flag
                sources['xwin'] = xwin
                sources['ywin'] = ywin

                # Calculate the average background at each source
                bkgflux, fluxerr, flag = sep.sum_ellipse(bkg.back(), sources['x'], sources['y'],
                                                         sources['a'], sources['b'], np.pi / 2.0,
                                                         2.5 * sources['kronrad'], subpix=1)
                #masksum, fluxerr, flag = sep.sum_ellipse(mask, sources['x'], sources['y'],
                #                                         sources['a'], sources['b'], np.pi / 2.0,
                #                                         2.5 * kronrad, subpix=1)

                background_area = (2.5 * sources['kronrad']) ** 2.0 * sources['a'] * sources['b'] * np.pi # - masksum
                sources['background'] = bkgflux
                sources['background'][background_area > 0] /= background_area[background_area > 0]
                # Update the catalog to match fits convention instead of python array convention
                sources['x'] += 1.0
                sources['y'] += 1.0

                sources['xpeak'] += 1
                sources['ypeak'] += 1

                sources['xwin'] += 1.0
                sources['ywin'] += 1.0

                sources['theta'] = np.degrees(sources['theta'])

                image.catalog = sources['x', 'y', 'xwin', 'ywin', 'xpeak', 'ypeak',
                                        'flux', 'fluxerr', 'background', 'fwhm',
                                        'a', 'b', 'theta', 'kronrad', 'ellipticity',
                                        'fluxrad25', 'fluxrad50', 'fluxrad75',
                                        'x2', 'y2', 'xy', 'flag']

                # Add the units and description to the catalogs
                image.catalog['x'].unit = 'pixel'
                image.catalog['x'].description = 'X coordinate of the object'
                image.catalog['y'].unit = 'pixel'
                image.catalog['y'].description = 'Y coordinate of the object'
                image.catalog['xwin'].unit = 'pixel'
                image.catalog['xwin'].description = 'Windowed X coordinate of the object'
                image.catalog['ywin'].unit = 'pixel'
                image.catalog['ywin'].description = 'Windowed Y coordinate of the object'
                image.catalog['xpeak'].unit = 'pixel'
                image.catalog['xpeak'].description = 'X coordinate of the peak'
                image.catalog['ypeak'].unit = 'pixel'
                image.catalog['ypeak'].description = 'Windowed Y coordinate of the peak'
                image.catalog['flux'].unit = 'counts'
                image.catalog['flux'].description = 'Flux within a Kron-like elliptical aperture'
                image.catalog['fluxerr'].unit = 'counts'
                image.catalog['fluxerr'].description = 'Erronr on the flux within a Kron-like elliptical aperture'
                image.catalog['background'].unit = 'counts'
                image.catalog['background'].description = 'Average background value in the aperture'
                image.catalog['fwhm'].unit = 'pixel'
                image.catalog['fwhm'].description = 'FWHM of the object'
                image.catalog['a'].unit = 'pixel'
                image.catalog['a'].description = 'Semi-major axis of the object'
                image.catalog['b'].unit = 'pixel'
                image.catalog['b'].description = 'Semi-minor axis of the object'
                image.catalog['theta'].unit = 'degrees'
                image.catalog['theta'].description = 'Position angle of the object'
                image.catalog['kronrad'].unit = 'pixel'
                image.catalog['kronrad'].description = 'Kron radius used for extraction'
                image.catalog['ellipticity'].description = 'Ellipticity'
                image.catalog['fluxrad25'].unit = 'pixel'
                image.catalog['fluxrad25'].description = 'Radius containing 25% of the flux'
                image.catalog['fluxrad50'].unit = 'pixel'
                image.catalog['fluxrad50'].description = 'Radius containing 50% of the flux'
                image.catalog['fluxrad75'].unit = 'pixel'
                image.catalog['fluxrad75'].description = 'Radius containing 75% of the flux'
                image.catalog['x2'].unit = 'pixel^2'
                image.catalog['x2'].description = 'Variance on X coordinate of the object'
                image.catalog['y2'].unit = 'pixel^2'
                image.catalog['y2'].description = 'Variance on Y coordinate of the object'
                image.catalog['xy'].unit = 'pixel^2'
                image.catalog['xy'].description = 'XY covariance of the object'
                image.catalog['flag'].description = 'Bit mask combination of extraction and photometry flags'

                image.catalog.sort('flux')
                image.catalog.reverse()

                logging_tags = logs.image_config_to_tags(image, self.group_by_keywords)
                logs.add_tag(logging_tags, 'filename', os.path.basename(image.filename))

                # Save some background statistics in the header
                mean_background = stats.sigma_clipped_mean(bkg.back(), 5.0)
                image.header['L1MEAN'] = (mean_background,
                                          '[counts] Sigma clipped mean of frame background')
                logs.add_tag(logging_tags, 'L1MEAN', float(mean_background))

                median_background = np.median(bkg.back())
                image.header['L1MEDIAN'] = (median_background,
                                            '[counts] Median of frame background')
                logs.add_tag(logging_tags, 'L1MEDIAN', float(median_background))

                std_background = stats.robust_standard_deviation(bkg.back())
                image.header['L1SIGMA'] = (std_background,
                                           '[counts] Robust std dev of frame background')
                logs.add_tag(logging_tags, 'L1SIGMA', float(std_background))

                # Save some image statistics to the header
                good_objects = image.catalog['flag'] == 0

                seeing = np.median(image.catalog['fwhm'][good_objects]) * image.pixel_scale
                image.header['L1FWHM'] = (seeing, '[arcsec] Frame FWHM in arcsec')
                logs.add_tag(logging_tags, 'L1FWHM', float(seeing))

                mean_ellipticity = stats.sigma_clipped_mean(sources['ellipticity'][good_objects],
                                                            3.0)
                image.header['L1ELLIP'] = (mean_ellipticity, 'Mean image ellipticity (1-B/A)')
                logs.add_tag(logging_tags, 'L1ELLIP', float(mean_ellipticity))

                mean_position_angle = stats.sigma_clipped_mean(sources['theta'][good_objects], 3.0)
                image.header['L1ELLIPA'] = (mean_position_angle,
                                            '[deg] PA of mean image ellipticity')
                logs.add_tag(logging_tags, 'L1ELLIPA', float(mean_position_angle))

                self.logger.info('Extracted sources', extra=logging_tags)

            except Exception as e:
                logging_tags = logs.image_config_to_tags(image, self.group_by_keywords)
                logs.add_tag(logging_tags, 'filename', os.path.basename(image.filename))
                self.logger.error(e, extra=logging_tags)
        return images
Example #41
0
for i in range(len(objects)):
    e = Ellipse(xy=(objects['x'][i], objects['y'][i]),
                width=6*objects['a'][i],
                height=6*objects['b'][i],
                angle=objects['theta'][i] * 180. / np.pi)
    e.set_facecolor('none')
    e.set_edgecolor('red')
    ax.add_artist(e)
theta=0
b =1
apertures = [2.5,3.4,5,7.5,10,15,20,30,50,70]
catalog = []
f = []
a =1
flux, fluxerr, flag = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[0],err=bkg.globalrms, gain=1.4)
flux1, fluxerr1, flag1 = sep.sum_ellipse(data_sub, objects['x'], objects['y'],  a, b, theta,apertures[1],err=bkg.globalrms, gain=1.4)
flux2, fluxerr2, flag2 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[2],err=bkg.globalrms, gain=1.4)
flux3, fluxerr3, flag3 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[3],err=bkg.globalrms, gain=1.4)
flux4, fluxerr4, flag4 = sep.sum_ellipse(data_sub, objects['x'], objects['y'],  a, b, theta,apertures[4],err=bkg.globalrms, gain=1.4)
flux5, fluxer5r, flag5 = sep.sum_ellipse(data_sub, objects['x'], objects['y'],  a, b, theta,apertures[5],err=bkg.globalrms, gain=1.4)
flux6, fluxerr6, flag6 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[6],err=bkg.globalrms, gain=1.4)
flux7, fluxerr7, flag7 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[7],err=bkg.globalrms, gain=1.4)
flux8, fluxerr8, flag8 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[8],err=bkg.globalrms, gain=1.4)
flux9, fluxerr9, flag9 = sep.sum_ellipse(data_sub, objects['x'], objects['y'], a, b, theta,apertures[9],err=bkg.globalrms, gain=1.4)
r, flag = sep.flux_radius(data_sub, objects['x'], objects['y'],6.*objects['a'], 0.5, normflux=flux, subpix=5)
r1, flag1 = sep.flux_radius(data_sub, objects['x'], objects['y'],6.*objects['a'], 0.5, normflux=flux, subpix=5)
r2, flag2 = sep.flux_radius(data_sub, objects['x'], objects['y'], 6.*objects['a'],0.5, normflux=flux, subpix=5)
r3, flag3 = sep.flux_radius(data_sub, objects['x'], objects['y'],6.*objects['a'], 0.5, normflux=flux, subpix=5)
r4, flag4 = sep.flux_radius(data_sub, objects['x'], objects['y'],6.*objects['a'], 0.5, normflux=flux, subpix=5)
r5, flag5 = sep.flux_radius(data_sub, objects['x'], objects['y'],6.*objects['a'], 0.5, normflux=flux, subpix=5)
Example #42
0
if not CONDENSED:
    print(blankline)

naper = 1000
nloop = 1
a = 1.
b = 1.
theta = np.pi/4.

for r in r_list:
    for subpix, method, label in subpix_list:
        line = "| ellipses r={0:2d}  {1:8s} |".format(int(r), label)

        t0 = time.time()
        flux, fluxerr, flag = sep.sum_ellipse(data, x, y, a, b, theta, r,
                                              subpix=subpix)
        t1 = time.time()
        t_sep = (t1-t0) * 1.e6 / naper / nloop
        line += " {0:7.2f} us/aper |".format(t_sep)

        if HAVE_PHOTUTILS:
            apertures = photutils.EllipticalAperture((x, y), a*r, b*r, theta)
            t0 = time.time()
            res = photutils.aperture_photometry(
                data, apertures, method=method, subpixels=subpix)
            t1 = time.time()
            t_pu = (t1-t0) * 1.e6 / naper
            line += " {0:7.2f} us/aper | {1:6.2f} |".format(t_pu, t_pu/t_sep)

        print(line)
Example #43
0
File: test.py Project: kbarbary/sep
def test_masked_segmentation_measurements():
    """Test measurements with segmentation masking"""
    
    NX = 100
    data = np.zeros((NX*2,NX*2))
    yp, xp = np.indices(data.shape)
    
    ####
    # Make two 2D gaussians that slightly overlap
    
    # width of the 2D objects
    gsigma = 10.  
      
    # offset between two gaussians in sigmas
    off = 4 

    for xy in [[NX,NX], [NX+off*gsigma, NX+off*gsigma]]:
        R = np.sqrt((xp-xy[0])**2+(yp-xy[1])**2)
        g_i = np.exp(-R**2/2/gsigma**2)
        data += g_i
    
    # Absolute total
    total_exact = g_i.sum()
    
    # Add some noise
    rms = 0.02
    np.random.seed(1)
    data += np.random.normal(size=data.shape)*rms
    
    # Run source detection
    objs, segmap = sep.extract(data, thresh=1.2, err=rms, mask=None,
                               segmentation_map=True)
    
    seg_id = np.arange(1, len(objs)+1, dtype=np.int32)
    
    # Compute Kron/Auto parameters
    x, y, a, b = objs['x'], objs['y'], objs['a'], objs['b']
    theta = objs['theta']
    
    kronrad, krflag = sep.kron_radius(data, x, y, a, b, theta, 6.0)
    
    flux_auto, fluxerr, flag = sep.sum_ellipse(data, x, y, a, b, theta,
                                               2.5*kronrad, 
                                               segmap=segmap, seg_id=seg_id, 
                                               subpix=1)
    
    # Test total flux
    assert_allclose(flux_auto, total_exact, rtol=5.e-2)
        
    # Flux_radius
    for flux_fraction in [0.2, 0.5]:
    
        # Exact solution
        rhalf_exact = np.sqrt(-np.log(1-flux_fraction)*gsigma**2*2)
    
        # Masked measurement
        flux_radius, flag = sep.flux_radius(data, x, y, 6.*a, flux_fraction,
                                        seg_id=seg_id, segmap=segmap, 
                                        normflux=flux_auto, subpix=5)
        
        # Test flux fraction
        assert_allclose(flux_radius, rhalf_exact, rtol=5.e-2)
    
    if False:
        print('test_masked_flux_radius')
        print(total_exact, flux_auto)
        print(rhalf_exact, flux_radius)