Beispiel #1
0
def measure_rms(coord, data, img_wcs, annulus_radius=0.1 * u.arcsecond):

    pixel_scale = np.abs(
        img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
    annulus_radius_pix = (annulus_radius.to(u.degree) /
                          pixel_scale).decompose()

    annulus_width = 15  #pix

    center_coord_pix = coord.to_pixel(img_wcs)
    cutout = Cutout2D(data,
                      center_coord_pix,
                      annulus_radius * 2.5,
                      img_wcs,
                      mode='partial')
    cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                     cutout.center_cutout[1])

    innerann_reg = regions.CirclePixelRegion(cutout_center,
                                             annulus_radius_pix.value)
    outerann_reg = regions.CirclePixelRegion(
        cutout_center, annulus_radius_pix.value + annulus_width)

    innerann_mask = innerann_reg.to_mask()

    annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout)

    # Calculate the SNR and aperture flux sums
    pixels_in_annulus = cutout.data[annulus_mask.astype('bool')]
    bg_rms = median_abs_deviation(pixels_in_annulus)

    return bg_rms
Beispiel #2
0
def mlla_nondet():
    MLLA_nondet = Table.read(
        '/home/jotter/nrao/summer_research_2018/tables/IR_nondet_may21_full.fits'
    )

    b3_fl = fits.open(
        '/home/jotter/nrao/images/Orion_SourceI_B3_continuum_r0.5.clean0.05mJy.allbaselines.huge.deepmask.image.tt0.pbcor.fits'
    )
    header = b3_fl[0].header
    img_wcs = WCS(header)
    data = b3_fl[0].data
    beam = radio_beam.Beam.from_fits_header(header)
    pixel_scale = np.abs(
        img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
    #ppbeam = (beam.sr/(pixel_scale**2)).decompose().value

    MLLA_coord = SkyCoord(ra=MLLA_nondet['RAJ2000'],
                          dec=MLLA_nondet['DEJ2000'],
                          unit=u.degree)
    annulus_radius = 0.1 * u.arcsecond
    annulus_radius_pix = (annulus_radius.to(u.degree) /
                          pixel_scale).decompose()

    annulus_width = 15  #pix

    ulim_fluxes = []

    for ind in range(len(MLLA_coord)):
        center_coord = MLLA_coord[ind]
        center_coord_pix = center_coord.to_pixel(img_wcs)
        cutout = Cutout2D(data,
                          center_coord_pix,
                          annulus_radius * 2.5,
                          img_wcs,
                          mode='partial')
        cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                         cutout.center_cutout[1])

        innerann_reg = regions.CirclePixelRegion(cutout_center,
                                                 annulus_radius_pix.value)
        outerann_reg = regions.CirclePixelRegion(
            cutout_center, annulus_radius_pix.value + annulus_width)

        innerann_mask = innerann_reg.to_mask()

        annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout)

        # Calculate the SNR and aperture flux sums
        pixels_in_annulus = cutout.data[annulus_mask.astype('bool')]
        bg_rms = median_abs_deviation(pixels_in_annulus)

        ulim_fluxes.append(3 * bg_rms)

    ulim_fluxes = np.array(ulim_fluxes) * 1000 * u.mJy  #in mJy
    MLLA_nondet['B3_flux_ulim'] = ulim_fluxes
    MLLA_nondet.write(
        '/home/jotter/nrao/summer_research_2018/tables/IR_nondet_may21_full_ulim.fits',
        overwrite=True)
def roi_to_reg(roi):
    """
    Function to convert a ROI to a region
    """

    if isinstance(roi, CircularROI):
        return regions.CirclePixelRegion(center=(roi.xc, roi.yc, radius=roi.radius))
    elif isinstance(roi, PointROI):
        return regions.PointRegion(center=(roi.x, roi.y))
    elif isinstance(roi, PolygonalROI):
        return regions.PolygonPixelRegion(vertices=list(zip(roi.vx, roi.vy)))
    else:
        raise NotImplementedError("ROI {0} not recognized".format(roi))
Beispiel #4
0
def create_region(rtype, args, dx, dy):
    if rtype in ["Rectangle", "Box"]:
        xctr, yctr, xw, yw = args
        x = xctr+dx
        y = yctr+dy
        center = regions.PixCoord(x=x, y=y)
        reg = regions.RectanglePixelRegion(center=center, width=xw, height=yw)
        bounds = [x-0.5*xw, x+0.5*xw, y-0.5*yw, y+0.5*yw]
    elif rtype == "Circle":
        xctr, yctr, radius = args
        x = xctr+dx
        y = yctr+dy
        center = regions.PixCoord(x=x, y=y)
        reg = regions.CirclePixelRegion(center=center, radius=radius)
        bounds = [x-radius, x+radius, y-radius, y+radius]
    elif rtype == "Polygon":
        x = np.array(args[0])+dx
        y = np.array(args[1])+dy
        vertices = regions.PixCoord(x=x, y=y)
        reg = regions.PolygonPixelRegion(vertices=vertices)
        bounds = [x.min(), x.max(), y.min(), y.max()]
    else:
        raise NotImplementedError
    return reg, bounds
def measure_fluxes_gaussfit_allsrcs(data,
                                    img,
                                    data_name,
                                    name_start=27,
                                    name_end=-21,
                                    ref_name='B3'):

    cat = Table.read(data)
    RA_name = 'gauss_x_' + ref_name

    ind = np.where(np.isnan(cat[RA_name]) == False)
    cat = cat[ind]

    col_names = fnmatch.filter(cat.colnames, 'ap_flux_r*')
    col_names = [cn[8:] for cn in col_names]

    fl = fits.open(img)
    header = fl[0].header
    img_data = fl[0].data.squeeze()
    mywcs = WCS(header).celestial

    beam = radio_beam.Beam.from_fits_header(header)
    pixel_scale = np.abs(
        mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
    ppbeam = (beam.sr / (pixel_scale**2)).decompose().value

    save_dir = '/lustre/aoc/students/jotter/gauss_diags/diff_imgs/' + ref_name + '/' + data_name + '/'
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    reg_file = '/users/jotter/summer_research_2018/final_regs/misc_regs/' + data_name + '_reg_file_' + ref_name + '_apflux.reg'
    with open(reg_file, 'w') as fh:
        fh.write("fk5\n")
        for ind in range(len(cat)):
            fh.write(
                'ellipse({x_cen}, {y_cen}, {maj}, {minr}, {ang}) #text={{{ID}}}\n'
                .format(x_cen=cat['gauss_x_' + ref_name][ind],
                        y_cen=cat['gauss_y_' + ref_name][ind],
                        maj=(cat['FWHM_major_' + ref_name][ind] *
                             u.arcsec.to(u.degree)),
                        minr=(cat['FWHM_minor_' + ref_name][ind] *
                              u.arcsec.to(u.degree)),
                        ang=cat['position_angle_' + ref_name][ind],
                        ID=str(cat['D_ID'][ind])))

    rad = Angle(1, 'arcsecond')  #radius for region list
    regs = []

    for ind in range(len(cat)):
        reg = regions.CircleSkyRegion(center=SkyCoord(
            cat['gauss_x_' + ref_name][ind] * u.degree,
            cat['gauss_y_' + ref_name][ind] * u.degree),
                                      radius=rad,
                                      meta={'text': str(cat['D_ID'][ind])})
        reg_pix = reg.to_pixel(mywcs)
        if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]):
            if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data):
                if np.isnan(img_data[int(reg_pix.center.x),
                                     int(reg_pix.center.y)]) == False:
                    regs.append(reg)

    cat_r = Angle(0.5, 'arcsecond')  #radius in gaussian fitting
    gauss_cat = gaussfit_catalog(
        img, regs, cat_r,
        savepath=save_dir)  #output is nested dictionary structure

    gauss_fit_tab = Table(
        names=('D_ID', 'FWHM_major_' + data_name, 'major_err_' + data_name,
               'FWHM_minor_' + data_name, 'minor_err_' + data_name,
               'pa_' + data_name, 'pa_err_' + data_name,
               'g_amplitude_' + data_name, 'amp_err_' + data_name),
        dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8'))
    for key in gauss_cat:
        gauss_fit_tab.add_row(
            (key, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'],
             gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'],
             gauss_cat[key]['pa'], gauss_cat[key]['e_pa'],
             gauss_cat[key]['amplitude'],
             gauss_cat[key]['e_amplitude']))  #fill table

    ap_flux_arr = []
    circ_flux_arr = []
    ap_flux_err_arr = []
    circ_flux_err_arr = []
    bg_median_arr = []
    bg_ap_arr = []
    bg_circ_arr = []
    RA = []
    DEC = []
    RA_err = []
    DEC_err = []

    for row in range(len(cat)):
        gauss_ind = np.where(gauss_fit_tab['D_ID'] == cat['D_ID'][row])[0]
        if len(gauss_ind) > 0:
            pix_major_fwhm = (
                (cat['FWHM_major_' + ref_name][row] * u.arcsec).to(u.degree) /
                pixel_scale).decompose()
            pix_minor_fwhm = (
                (cat['FWHM_minor_' + ref_name][row] * u.arcsec).to(u.degree) /
                pixel_scale).decompose()

            center_coord = SkyCoord(cat['gauss_x_' + ref_name][row],
                                    cat['gauss_y_' + ref_name][row],
                                    frame='icrs',
                                    unit=(u.deg, u.deg))
            center_coord_pix = center_coord.to_pixel(mywcs)
            center_coord_pix_reg = regions.PixCoord(center_coord_pix[0],
                                                    center_coord_pix[1])
            position_angle = cat['position_angle_' + ref_name][row] * u.deg

            ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg,
                                                     pix_major_fwhm * 2.,
                                                     pix_minor_fwhm * 2.,
                                                     angle=position_angle)
            size = pix_major_fwhm * 2.1
            ap_mask = ellipse_reg.to_mask()
            cutout_mask = ap_mask.cutout(img_data)

            aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam
            npix = len(cutout_mask[ap_mask.data == 1])
            ap_flux_arr.append(aperture_flux)

            #now creating annulus around source to measure background and ap flux error
            annulus_width = 15
            annulus_radius = 0.1 * u.arcsecond
            annulus_radius_pix = (annulus_radius.to(u.degree) /
                                  pixel_scale).decompose()

            # Cutout section of the image we care about, to speed up computation time
            size = 2.5 * annulus_radius
            cutout = Cutout2D(img_data,
                              center_coord_pix,
                              size,
                              mywcs,
                              mode='partial')  #cutout of outer circle
            cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                             cutout.center_cutout[1])

            # Define the aperture regions needed for SNR
            innerann_reg = regions.CirclePixelRegion(cutout_center,
                                                     annulus_radius_pix)
            outerann_reg = regions.CirclePixelRegion(
                cutout_center, annulus_radius_pix + annulus_width)

            # Make masks from aperture regions
            annulus_mask = mask(outerann_reg, cutout) - mask(
                innerann_reg, cutout)

            # Calculate the SNR and aperture flux sums
            pixels_in_annulus = cutout.data[annulus_mask.astype(
                'bool')]  #pixels within annulus
            bg_rms = rms(pixels_in_annulus)
            ap_bg_rms = bg_rms / np.sqrt(
                npix / ppbeam)  #rms/sqrt(npix/ppbeam) - rms error per beam
            bg_median = np.median(pixels_in_annulus)

            pix_bg = bg_median * npix / ppbeam

            ap_flux_err_arr.append(ap_bg_rms)
            bg_median_arr.append(bg_median)
            bg_ap_arr.append(pix_bg)

            #now measure circle flux:
            radius = 0.1 * u.arcsecond
            radius_pix = annulus_radius_pix
            circle_reg = regions.CirclePixelRegion(center_coord_pix_reg,
                                                   radius_pix)
            circ_ap_mask = circle_reg.to_mask()
            circ_cutout_mask = circ_ap_mask.cutout(img_data)
            cutout_mask = ap_mask.cutout(img_data)
            circ_aperture_flux = np.sum(
                circ_cutout_mask[circ_ap_mask.data == 1]) / ppbeam
            circ_npix = len(circ_cutout_mask[circ_ap_mask.data == 1])

            circ_bg_rms = bg_rms / np.sqrt(circ_npix / ppbeam)

            circ_flux_arr.append(circ_aperture_flux)
            circ_flux_err_arr.append(circ_bg_rms)
            bg_circ_arr.append(bg_median * circ_npix / ppbeam)

            RA.append(cat['gauss_x_' + ref_name][row])
            DEC.append(cat['gauss_y_' + ref_name][row])
            RA_err.append(cat['x_err_' + ref_name][row])
            DEC_err.append(cat['y_err_' + ref_name][row])

    cols = [
        'ap_flux_', 'ap_flux_err_', 'bg_median_', 'bg_ap_', 'circ_flux_',
        'circ_flux_err_', 'bg_circ_'
    ]
    arrs = [
        ap_flux_arr, ap_flux_err_arr, bg_median_arr, bg_ap_arr, circ_flux_arr,
        circ_flux_err_arr, bg_circ_arr
    ]
    cols2 = ['RA_', 'DEC_', 'RA_err_',
             'DEC_err_']  #seperate bc different naming
    arrs2 = [RA, DEC, RA_err, DEC_err]
    for j in range(len(cols)):
        gauss_fit_tab[cols[j] + data_name] = arrs[j]
    for c in range(len(cols2)):
        gauss_fit_tab[cols2[c] + ref_name] = arrs2[c]

    gauss_fit_tab.write('/lustre/aoc/students/jotter/dendro_catalogs/' +
                        data_name + '_500klplus_allsrcs_catalog_' + ref_name +
                        '_ref.txt',
                        format='ascii',
                        overwrite=True)
def measure_fluxes_gaussfit(data,
                            directory,
                            data_name,
                            name_start=27,
                            name_end=-21,
                            ref_name='B6'):

    #This function takes a directory full of images and adds them all to a catalog with flux measurements and fits gaussians. 'data' is the catalog to get positions from, 'ref_name' is the name of the reference data (for positions), 'directory' is the directory with all the new images.

    cat = Table.read(data)
    RA_names = ['gauss_x_B3', 'gauss_x_B6', 'gauss_x_B7_hr']
    inds = []
    for fn in RA_names:
        ind = np.where(np.isnan(cat[fn]) == False)
        inds.append(ind)
    detected = reduce(
        np.intersect1d, inds
    )  #sources detected in B3, B6, B7 - only make measurements on these sources
    cat = cat[detected]

    imgs = glob.glob(directory + '*r-2.clean0.1mJy.500klplus.deepmask*')
    img_names = [img[len(directory) + name_start:name_end] for img in imgs]

    col_names = fnmatch.filter(cat.colnames, 'ap_flux_r*')
    col_names = [cn[8:] for cn in col_names]

    for j, img in enumerate(imgs):
        name = img_names[j]
        if len(name) > 54:
            name = name[0:53]
        if name not in col_names:
            fl = fits.open(img)
            header = fl[0].header
            try:
                img_data = fl[0].data.squeeze()
            except TypeError:
                print("error! img: " + img)
                continue
            mywcs = WCS(header).celestial

            beam = radio_beam.Beam.from_fits_header(header)
            pixel_scale = np.abs(
                mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
            ppbeam = (beam.sr / (pixel_scale**2)).decompose().value

            save_dir = '/lustre/aoc/students/jotter/gauss_diags/diff_imgs/' + ref_name + '/' + name + '/'
            if not os.path.exists(save_dir):
                os.makedirs(save_dir)

            reg_file = '/users/jotter/summer_research_2018/final_regs/' + name + '_reg_file_B6_apflux.reg'
            with open(reg_file, 'w') as fh:
                fh.write("fk5\n")
                for ind in range(len(cat)):
                    fh.write(
                        'ellipse({x_cen}, {y_cen}, {maj}, {minr}, {ang}) #text={{{ID}}}\n'
                        .format(x_cen=cat['gauss_x_' + ref_name][ind],
                                y_cen=cat['gauss_y_' + ref_name][ind],
                                maj=(cat['FWHM_major_' + ref_name][ind] *
                                     u.arcsec.to(u.degree)),
                                minr=(cat['FWHM_minor_' + ref_name][ind] *
                                      u.arcsec.to(u.degree)),
                                ang=cat['position_angle_' + ref_name][ind],
                                ID=str(cat['D_ID'][ind])))

            rad = Angle(1, 'arcsecond')  #radius for region list
            regs = []

            for ind in range(len(cat)):
                reg = regions.CircleSkyRegion(center=SkyCoord(
                    cat['gauss_x_' + ref_name][ind] * u.degree,
                    cat['gauss_y_' + ref_name][ind] * u.degree),
                                              radius=rad,
                                              meta={
                                                  'text': str(cat['D_ID'][ind])
                                              })
                reg_pix = reg.to_pixel(mywcs)
                if reg_pix.center.x > 0 and reg_pix.center.x < len(
                        img_data[0]):
                    if reg_pix.center.y > 0 and reg_pix.center.y < len(
                            img_data):
                        regs.append(reg)

            cat_r = Angle(0.5, 'arcsecond')  #radius in gaussian fitting
            gauss_cat = gaussfit_catalog(
                img, regs, cat_r,
                savepath=save_dir)  #output is nested dictionary structure

            gauss_fit_tab = Table(
                names=('D_ID', 'FWHM_major_' + name, 'major_err_' + name,
                       'FWHM_minor_' + name, 'minor_err_' + name, 'pa_' + name,
                       'pa_err_' + name, 'g_amplitude_' + name,
                       'amp_err_' + name),
                dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8'))
            for key in gauss_cat:
                gauss_fit_tab.add_row(
                    (key, gauss_cat[key]['fwhm_major'],
                     gauss_cat[key]['e_fwhm_major'],
                     gauss_cat[key]['fwhm_minor'],
                     gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'],
                     gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'],
                     gauss_cat[key]['e_amplitude']))  #fill table

            cat = join(cat, gauss_fit_tab, keys='D_ID', join_type='left')

            ap_flux_arr = []
            circ_flux_arr = []
            ap_flux_err_arr = []
            circ_flux_err_arr = []
            bg_median_arr = []
            bg_ap_arr = []
            bg_circ_arr = []

            for row in range(len(cat)):
                pix_major_fwhm = ((cat['FWHM_major_' + ref_name][row] *
                                   u.arcsec).to(u.degree) /
                                  pixel_scale).decompose()
                pix_minor_fwhm = ((cat['FWHM_minor_' + ref_name][row] *
                                   u.arcsec).to(u.degree) /
                                  pixel_scale).decompose()

                center_coord = SkyCoord(cat['gauss_x_' + ref_name][row],
                                        cat['gauss_y_' + ref_name][row],
                                        frame='icrs',
                                        unit=(u.deg, u.deg))
                center_coord_pix = center_coord.to_pixel(mywcs)
                center_coord_pix_reg = regions.PixCoord(
                    center_coord_pix[0], center_coord_pix[1])
                position_angle = cat['position_angle_' + ref_name][row] * u.deg
                print(center_coord_pix_reg, pix_major_fwhm, pix_minor_fwhm)
                ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg,
                                                         pix_major_fwhm * 2.,
                                                         pix_minor_fwhm * 2.,
                                                         angle=position_angle)
                size = pix_major_fwhm * 2.1
                ap_mask = ellipse_reg.to_mask()
                cutout_mask = ap_mask.cutout(img_data)

                aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam
                npix = len(cutout_mask[ap_mask.data == 1])
                ap_flux_arr.append(aperture_flux)

                #now creating annulus around source to measure background and ap flux error
                annulus_width = 15
                annulus_radius = 0.1 * u.arcsecond
                annulus_radius_pix = (annulus_radius.to(u.degree) /
                                      pixel_scale).decompose()

                # Cutout section of the image we care about, to speed up computation time
                size = 2.5 * annulus_radius
                cutout = Cutout2D(img_data,
                                  center_coord_pix,
                                  size,
                                  mywcs,
                                  mode='partial')  #cutout of outer circle
                cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                                 cutout.center_cutout[1])

                # Define the aperture regions needed for SNR
                innerann_reg = regions.CirclePixelRegion(
                    cutout_center, annulus_radius_pix)
                outerann_reg = regions.CirclePixelRegion(
                    cutout_center, annulus_radius_pix + annulus_width)

                # Make masks from aperture regions
                annulus_mask = mask(outerann_reg, cutout) - mask(
                    innerann_reg, cutout)

                # Calculate the SNR and aperture flux sums
                pixels_in_annulus = cutout.data[annulus_mask.astype(
                    'bool')]  #pixels within annulus
                bg_rms = rms(pixels_in_annulus)
                ap_bg_rms = bg_rms / np.sqrt(
                    npix / ppbeam)  #rms/sqrt(npix/ppbeam) - rms error per beam
                bg_median = np.median(pixels_in_annulus)

                pix_bg = bg_median * npix / ppbeam

                ap_flux_err_arr.append(ap_bg_rms)
                bg_median_arr.append(bg_median)
                bg_ap_arr.append(pix_bg)

                #now measure circle flux:
                radius = 0.1 * u.arcsecond
                radius_pix = annulus_radius_pix
                circle_reg = regions.CirclePixelRegion(center_coord_pix_reg,
                                                       radius_pix)
                circ_ap_mask = circle_reg.to_mask()
                circ_cutout_mask = circ_ap_mask.cutout(img_data)
                cutout_mask = ap_mask.cutout(img_data)
                circ_aperture_flux = np.sum(
                    circ_cutout_mask[circ_ap_mask.data == 1]) / ppbeam
                circ_npix = len(circ_cutout_mask[circ_ap_mask.data == 1])

                circ_bg_rms = bg_rms / np.sqrt(circ_npix / ppbeam)

                circ_flux_arr.append(circ_aperture_flux)
                circ_flux_err_arr.append(circ_bg_rms)
                bg_circ_arr.append(bg_median * circ_npix / ppbeam)

            cols = [
                'ap_flux_', 'ap_flux_err_', 'bg_median_', 'bg_ap_',
                'circ_flux_', 'circ_flux_err_', 'bg_circ_'
            ]
            arrs = [
                ap_flux_arr, ap_flux_err_arr, bg_median_arr, bg_ap_arr,
                circ_flux_arr, circ_flux_err_arr, bg_circ_arr
            ]
            for j in range(len(cols)):
                cat[cols[j] + name] = arrs[j]

    cat.write('/lustre/aoc/students/jotter/dendro_catalogs/' + data_name +
              '_500klplus_allsrcs_catalog_' + ref_name + '_ref.txt',
              format='ascii',
              overwrite=True)
def measure_ap_fluxes(data,
                      directory,
                      data_name,
                      name_start=27,
                      name_end=-21,
                      ref_name='B6'):

    #This function takes a directory full of images and adds them all to a catalog with flux measurements. 'data' is the catalog to get positions and sizes from, 'ref_name' is the name of the reference data (for positions), 'directory' is the directory with all the new images.
    #data = '/lustre/aoc/students/jotter/dendro_catalogs/IR_matched_catalog_B7.fits'
    #directory = '/lustre/aoc/students/jotter/directory/OrionB3/'

    cat = Table.read(data)
    RA_names = ['gauss_x_B3', 'gauss_x_B6', 'gauss_x_B7_hr']
    inds = []
    for fn in RA_names:
        ind = np.where(np.isnan(cat[fn]) == False)
        inds.append(ind)
    detected = reduce(
        np.intersect1d, inds
    )  #sources detected in B3, B6, B7 - only make measurements on these sources
    cat = cat[detected]

    imgs = glob.glob(directory + '*')
    img_names = [img[len(directory) + name_start:name_end] for img in imgs]

    col_names = fnmatch.filter(cat.colnames, 'ap_flux_r*')
    col_names = [cn[8:] for cn in col_names]

    for j, img in enumerate(imgs):
        name = img_names[j]
        if len(name) > 54:
            name = name[0:53]
        if name not in col_names:
            fl = fits.open(img)
            header = fl[0].header
            try:
                img_data = fl[0].data.squeeze()
            except TypeError:
                print("ERROR! img: " + img)
                continue
            mywcs = WCS(header).celestial

            beam = radio_beam.Beam.from_fits_header(header)
            pixel_scale = np.abs(
                mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
            ppbeam = (beam.sr / (pixel_scale**2)).decompose().value

            ap_flux_arr = []
            circ_flux_arr = []
            ap_flux_err_arr = []
            circ_flux_err_arr = []
            bg_median_arr = []
            bg_ap_arr = []
            bg_circ_arr = []
            for row in range(len(cat)):
                pix_major_fwhm = ((cat['FWHM_major_' + ref_name][row] *
                                   u.arcsec).to(u.degree) /
                                  pixel_scale).decompose()
                pix_minor_fwhm = ((cat['FWHM_minor_' + ref_name][row] *
                                   u.arcsec).to(u.degree) /
                                  pixel_scale).decompose()

                center_coord = SkyCoord(cat['gauss_x_' + ref_name][row],
                                        cat['gauss_y_' + ref_name][row],
                                        frame='icrs',
                                        unit=(u.deg, u.deg))
                center_coord_pix = center_coord.to_pixel(mywcs)
                center_coord_pix_reg = regions.PixCoord(
                    center_coord_pix[0], center_coord_pix[1])
                position_angle = cat['position_angle_' + ref_name][row] * u.deg
                ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg,
                                                         pix_major_fwhm * 2.,
                                                         pix_minor_fwhm * 2.,
                                                         angle=position_angle)
                size = pix_major_fwhm * 2.1
                ap_mask = ellipse_reg.to_mask()
                cutout_mask = ap_mask.cutout(img_data)

                aperture_flux = np.sum(cutout_mask[ap_mask.data == 1]) / ppbeam
                npix = len(cutout_mask[ap_mask.data == 1])
                ap_flux_arr.append(aperture_flux)

                #now creating annulus around source to measure background and ap flux error
                annulus_width = 15
                annulus_radius = 0.1 * u.arcsecond
                annulus_radius_pix = (annulus_radius.to(u.degree) /
                                      pixel_scale).decompose()

                # Cutout section of the image we care about, to speed up computation time
                size = 2.5 * annulus_radius
                cutout = Cutout2D(img_data,
                                  center_coord_pix,
                                  size,
                                  mywcs,
                                  mode='partial')  #cutout of outer circle
                cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                                 cutout.center_cutout[1])

                # Define the aperture regions needed for SNR
                innerann_reg = regions.CirclePixelRegion(
                    cutout_center, annulus_radius_pix)
                outerann_reg = regions.CirclePixelRegion(
                    cutout_center, annulus_radius_pix + annulus_width)

                # Make masks from aperture regions
                annulus_mask = mask(outerann_reg, cutout) - mask(
                    innerann_reg, cutout)

                # Calculate the SNR and aperture flux sums
                pixels_in_annulus = cutout.data[annulus_mask.astype(
                    'bool')]  #pixels within annulus
                bg_rms = rms(pixels_in_annulus)
                ap_bg_rms = bg_rms / np.sqrt(
                    npix / ppbeam)  #rms/sqrt(npix/ppbeam) - rms error per beam
                bg_median = np.median(pixels_in_annulus)

                pix_bg = bg_median * npix / ppbeam

                ap_flux_err_arr.append(ap_bg_rms)
                bg_median_arr.append(bg_median)
                bg_ap_arr.append(pix_bg)

                #now measure circle flux:
                radius = 0.1 * u.arcsecond
                radius_pix = annulus_radius_pix
                circle_reg = regions.CirclePixelRegion(center_coord_pix_reg,
                                                       radius_pix)
                circ_ap_mask = circle_reg.to_mask()
                circ_cutout_mask = circ_ap_mask.cutout(img_data)
                cutout_mask = ap_mask.cutout(img_data)
                circ_aperture_flux = np.sum(
                    circ_cutout_mask[circ_ap_mask.data == 1]) / ppbeam
                circ_npix = len(circ_cutout_mask[circ_ap_mask.data == 1])

                circ_bg_rms = bg_rms / np.sqrt(circ_npix / ppbeam)

                circ_flux_arr.append(circ_aperture_flux)
                circ_flux_err_arr.append(circ_bg_rms)
                bg_circ_arr.append(bg_median * circ_npix / ppbeam)

            cols = [
                'ap_flux_', 'ap_flux_err_', 'bg_median_', 'bg_ap_',
                'circ_flux_', 'circ_flux_err_', 'bg_circ_'
            ]
            arrs = [
                ap_flux_arr, ap_flux_err_arr, bg_median_arr, bg_ap_arr,
                circ_flux_arr, circ_flux_err_arr, bg_circ_arr
            ]
            for j in range(len(cols)):
                cat[cols[j] + name] = arrs[j]

    cat.write('/lustre/aoc/students/jotter/dendro_catalogs/' + data_name +
              '_diff_imgs_catalog_' + ref_name + '_ref.txt',
              format='ascii',
              overwrite=True)
Beispiel #8
0
def b6b7_catalog(B6_img, B6_name, B7_img, B7_name, cat_name, nonconv_B6_img=None, nonconv_B7_img=None):
    #creates catalog from one image in each band
    #B3_names, B6_names, B7_names only used for gaussian diag directory names
    
    ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/ref_catalog_may21.fits'
    ref_data = Table.read(ref_data_name)
    ref_arrs = [ref_data['B6_detect'], ref_data['B7_detect']]
  
    band_imgs = [B6_img, B7_img]
    band_names = ['B6', 'B7']
    band_img_names = [B6_name, B7_name]
    band_tables = []
    for b in range(len(band_imgs)): #first loop through different bands
        name = band_names[b]
        img_name = band_img_names[b]
        img = band_imgs[b]
        fl = fits.open(img)
        header = fl[0].header
        img_data = fl[0].data.squeeze()
        img_wcs = WCS(header).celestial

        beam = radio_beam.Beam.from_fits_header(header)
        pixel_scale = np.abs(img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
        ppbeam = (beam.sr/(pixel_scale**2)).decompose().value

        if name == 'B6' and nonconv_B6_img is not None:
            fl = fits.open(nonconv_B6_img)
            header = fl[0].header
            nonconv_beam = radio_beam.Beam.from_fits_header(header)
            ppbeam = (nonconv_beam.sr/(pixel_scale**2)).decompose().value
        if name == 'B7' and nonconv_B7_img is not None:
            fl = fits.open(nonconv_B7_img)
            header = fl[0].header
            nonconv_beam = radio_beam.Beam.from_fits_header(header)
            ppbeam = (nonconv_beam.sr/(pixel_scale**2)).decompose().value
            
        #now get ready to fit gaussians
        #start by setting up save directory for images
        gauss_save_dir = '/home/jotter/nrao/gauss_diags_may21/'+img_name+'/'
        if not os.path.exists(gauss_save_dir):
            os.makedirs(gauss_save_dir)
        #now make region list

        rad = Angle(1, 'arcsecond') #radius used in region list
        regs = []

        src_inds = np.where(ref_arrs[b] == True)[0]
        print(len(src_inds))

        for ind in src_inds:
            reg = regions.CircleSkyRegion(center=SkyCoord(ref_data['RA_B3'][ind]*u.degree, ref_data['DEC_B3'][ind]*u.degree), radius=rad, meta={'text':str(ref_data['B3_Seq'][ind])})
            reg_pix = reg.to_pixel(img_wcs)
            if reg_pix.center.x > 0 and reg_pix.center.x < len(img_data[0]):
                if reg_pix.center.y > 0 and reg_pix.center.y < len(img_data):
                    if np.isnan(img_data[int(reg_pix.center.x), int(reg_pix.center.y)]) == False:
                        regs.append(reg)

        cat_r = Angle(0.5, 'arcsecond')/2 #radius for gaussian fitting
        print('ok')
        gauss_cat = gaussfit_catalog(img, regs, cat_r, savepath=gauss_save_dir, max_offset_in_beams = 1, max_radius_in_beams = 5)
        #table does not have all columns yet, add others later
        img_table = Table(names=('Seq_B3', 'fwhm_maj_'+name, 'fwhm_maj_err_'+name, 'fwhm_min_'+name, 'fwhm_min_err_'+name, 'pa_'+name, 'pa_err_'+name, 'gauss_amp_'+name, 'gauss_amp_err_'+name,'RA_'+name,'RA_err_'+name, 'DEC_'+name, 'DEC_err_'+name, ), dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8'))
        for key in gauss_cat:
            img_table.add_row((key, gauss_cat[key]['fwhm_major'], gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'], gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'], gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'], gauss_cat[key]['e_amplitude'], gauss_cat[key]['center_x'], gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'], gauss_cat[key]['e_center_y']))
        #now measure deconvovled sizes and aperture flux measurements for each source 
        ap_flux_arr = []
        ap_flux_err_arr = []
        fwhm_maj_deconv_arr = []
        fwhm_maj_deconv_err_arr = []
        fwhm_min_deconv_arr = []
        fwhm_min_deconv_err_arr = []
        pa_deconv_arr = []
        pa_deconv_err_arr = []
        snr_arr = []
        rms_arr = []
 
        for row in range(len(img_table)): #now loop through sources in reference data and make measurements
            ref_ind = np.where(ref_data['B3_Seq'] == img_table['Seq_B3'][row])[0]
            if len(ref_ind > 0):
                #now measuring deconvolved sizes
                measured_source_size = radio_beam.Beam(major=img_table['fwhm_maj_'+name][row]*u.arcsec, minor=img_table['fwhm_min_'+name][row]*u.arcsec, pa=(img_table['pa_'+name][row]-90)*u.degree)
                try:
                    deconv_size = measured_source_size.deconvolve(beam)
                    fwhm_maj_deconv_arr.append(deconv_size.major.value)
                    fwhm_min_deconv_arr.append(deconv_size.minor.value)
                    fwhm_maj_deconv_err_arr.append(img_table['fwhm_maj_err_'+name][row]) #same error as non deconvolved
                    fwhm_min_deconv_err_arr.append(img_table['fwhm_min_err_'+name][row])
                    pa_deconv_arr.append(deconv_size.pa.to(u.deg).value)
                    pa_deconv_err_arr.append(img_table['pa_err_'+name][row])
                except ValueError:
                    fwhm_maj_deconv_arr.append(np.nan)
                    fwhm_min_deconv_arr.append(np.nan)
                    fwhm_maj_deconv_err_arr.append(np.nan)
                    fwhm_min_deconv_err_arr.append(np.nan)
                    pa_deconv_arr.append(np.nan)
                    pa_deconv_err_arr.append(np.nan)
 

                pix_major_fwhm = ((img_table['fwhm_maj_'+name][row]*u.arcsec).to(u.degree)/pixel_scale).decompose()
                pix_minor_fwhm = ((img_table['fwhm_min_'+name][row]*u.arcsec).to(u.degree)/pixel_scale).decompose()
                center_coord = SkyCoord(img_table['RA_'+name][row], img_table['DEC_'+name][row], frame='icrs', unit=(u.deg, u.deg))
                center_coord_pix = center_coord.to_pixel(img_wcs)
                center_coord_pix_reg = regions.PixCoord(center_coord_pix[0], center_coord_pix[1])
                pos_ang = (img_table['pa_'+name][row]-90)*u.deg #must subtract 90 to be consistent

                ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg, pix_major_fwhm.value*2, pix_minor_fwhm.value*2, angle=pos_ang)
                ap_mask = ellipse_reg.to_mask()
                cutout_mask = ap_mask.cutout(img_data)
                
                aperture_flux = np.nansum(cutout_mask[ap_mask.data==1])/ppbeam
                npix = len(cutout_mask[ap_mask.data==1])

                #now make annulus for measuring background and error
                annulus_width = 15 #pixels
                annulus_radius = img_table['fwhm_maj_'+name][row]*u.arcsecond#+0.05*u.arcsecond
                annulus_radius_pix = (annulus_radius.to(u.degree)/pixel_scale).decompose()

                #cutout image
                cutout = Cutout2D(img_data, center_coord_pix, annulus_radius*2.5, img_wcs, mode='partial')
                cutout_center = regions.PixCoord(cutout.center_cutout[0], cutout.center_cutout[1])

                #define aperture regions for SNR
                innerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix.value)
                outerann_reg = regions.CirclePixelRegion(cutout_center, annulus_radius_pix.value+annulus_width)

                #Make masks from aperture regions
                annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout)

                # Calculate the SNR and aperture flux sums
                pixels_in_annulus = cutout.data[annulus_mask.astype('bool')]
                bg_rms = median_abs_deviation(pixels_in_annulus)
                print(img_table['Seq_B3'][row])
                print('BG RMS: %f' % (bg_rms))
                ap_bg_rms = bg_rms/np.sqrt(npix/ppbeam) #rms/sqrt(npix/ppbeam) - rms error per beam
                bg_median = np.nanmedian(pixels_in_annulus)

                pix_bg = bg_median*npix/ppbeam

                ap_flux_bgcorrect = aperture_flux - pix_bg
                ap_flux_correct = ap_flux_bgcorrect + ap_flux_bgcorrect*(1 - special.erf(2*np.sqrt(np.log(2)))) #flux correction for summing within 2*fwhm
                
                ap_flux_err_arr.append(ap_bg_rms)
                ap_flux_arr.append(ap_flux_correct)
                snr_arr.append(img_table['gauss_amp_'+name][row]/bg_rms)
                rms_arr.append(bg_rms)
                
        cols = ['ap_flux_'+name, 'ap_flux_err_'+name, 'fwhm_maj_deconv_'+name, 'fwhm_maj_deconv_err_'+name, 'fwhm_min_deconv_'+name, 'fwhm_min_deconv_err_'+name, 'pa_deconv_'+name, 'pa_deconv_err_'+name, 'SNR_'+name, 'RMS_'+name]
        arrs = [ap_flux_arr, ap_flux_err_arr, fwhm_maj_deconv_arr, fwhm_maj_deconv_err_arr, fwhm_min_deconv_arr, fwhm_min_deconv_err_arr, pa_deconv_arr, pa_deconv_err_arr, snr_arr, rms_arr]
        for c in range(len(cols)):
            img_table.add_column(Column(np.array(arrs[c])), name=cols[c])
        img_table.add_column(Column(np.array(fwhm_maj_deconv_arr)/np.array(fwhm_min_deconv_arr)), name='ar_deconv_'+name)
        band_tables.append(img_table) #list of tables for each image
            
    
    B6B7 = join(band_tables[0], band_tables[1], keys='Seq_B3', join_type='outer')
    B6B7.write('/home/jotter/nrao/summer_research_2018/tables/'+cat_name+'.fits',  overwrite=True)
Beispiel #9
0
def reject(imfile, catfile, threshold):
    """Reject noisy detections.
    
    Parameters
    ----------
    imfile : str
        The path to the radio image file
    catfile : str
        The path to the source catalog, as obtained from detect.py
    threshold : float
        The signal-to-noise threshold below which sources are rejected
    """
    # Extract information from filename
    outfile = os.path.basename(catfile).split('cat_')[1].split('.dat')[0]
    region = outfile.split('region')[1].split('_band')[0]
    band = outfile.split('band')[1].split('_val')[0]
    min_value = outfile.split('val')[1].split('_delt')[0]
    min_delta = outfile.split('delt')[1].split('_pix')[0]
    min_npix = outfile.split('pix')[1]
    print("\nSource rejection for region {} in band {}".format(region, band))

    print("Loading image file")
    contfile = fits.open(imfile)
    data = contfile[0].data.squeeze()
    mywcs = wcs.WCS(contfile[0].header).celestial

    catalog = Table(Table.read(catfile, format='ascii'), masked=True)

    beam = radio_beam.Beam.from_fits_header(contfile[0].header)
    pixel_scale = np.abs(
        mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
    ppbeam = (beam.sr / (pixel_scale**2)).decompose().value

    data = data / ppbeam

    # Remove existing region files
    if os.path.isfile('./reg/reg_' + outfile + '_annulus.reg'):
        os.remove('./reg/reg_' + outfile + '_annulus.reg')
    if os.path.isfile('./reg/reg_' + outfile + '_filtered.reg'):
        os.remove('./reg/reg_' + outfile + '_filtered.reg')

    # Load in manually accepted and rejected sources
    override_accepted = []
    override_rejected = []
    if os.path.isfile('./.override/accept_' + outfile + '.txt'):
        override_accepted = np.loadtxt('./.override/accept_' + outfile +
                                       '.txt').astype('int')
    if os.path.isfile('./.override/reject_' + outfile + '.txt'):
        override_rejected = np.loadtxt('./.override/reject_' + outfile +
                                       '.txt').astype('int')
    print("\nManually accepted sources: ", set(override_accepted))
    print("Manually rejected sources: ", set(override_rejected))

    print('\nCalculating RMS values within aperture annuli')
    pb = ProgressBar(len(catalog))

    data_cube = []
    masks = []
    rejects = []
    snr_vals = []
    mean_backgrounds = []

    for i in range(len(catalog)):
        x_cen = catalog['x_cen'][i] * u.deg
        y_cen = catalog['y_cen'][i] * u.deg
        major_fwhm = catalog['major_fwhm'][i] * u.deg
        minor_fwhm = catalog['minor_fwhm'][i] * u.deg
        position_angle = catalog['position_angle'][i] * u.deg
        dend_flux = catalog['dend_flux_band{}'.format(band)][i]

        annulus_width = 1e-5 * u.deg
        center_distance = 1e-5 * u.deg

        # Define some ellipse properties in pixel coordinates
        position = coordinates.SkyCoord(x_cen,
                                        y_cen,
                                        frame='icrs',
                                        unit=(u.deg, u.deg))
        pix_position = np.array(position.to_pixel(mywcs))
        pix_major_fwhm = major_fwhm / pixel_scale
        pix_minor_fwhm = minor_fwhm / pixel_scale

        # Cutout section of the image we care about, to speed up computation time
        size = (center_distance + annulus_width + major_fwhm) * 2.2
        cutout = Cutout2D(data, position, size, mywcs, mode='partial')
        cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                         cutout.center_cutout[1])

        # Define the aperture regions needed for SNR
        ellipse_reg = regions.EllipsePixelRegion(
            cutout_center,
            pix_major_fwhm * 2.,
            pix_minor_fwhm * 2.,
            angle=position_angle
        )  # Make sure you're running the dev version of regions, otherwise the position angles will be in radians!

        innerann_reg = regions.CirclePixelRegion(
            cutout_center, center_distance / pixel_scale + pix_major_fwhm)
        outerann_reg = regions.CirclePixelRegion(
            cutout_center, center_distance / pixel_scale + pix_major_fwhm +
            annulus_width / pixel_scale)

        # Make masks from aperture regions
        ellipse_mask = mask(ellipse_reg, cutout)
        annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout)

        # Plot annulus and ellipse regions
        data_cube.append(cutout.data)
        masks.append([annulus_mask, ellipse_mask])

        # Calculate the SNR and aperture flux sums
        bg_rms = rms(cutout.data[annulus_mask.astype('bool')])
        peak_flux = np.max(cutout.data[ellipse_mask.astype('bool')])
        flux_rms_ratio = peak_flux / bg_rms
        snr_vals.append(flux_rms_ratio)

        # Reject bad sources below some SNR threshold
        rejected = False
        if flux_rms_ratio <= threshold:
            rejected = True

        # Process manual overrides
        if catalog['_idx'][i] in override_accepted:
            rejected = False
        if catalog['_idx'][i] in override_rejected:
            rejected = True
        rejects.append(int(rejected))

        # Add non-rejected source ellipses to a new region file
        fname = './reg/reg_' + outfile + '_filtered.reg'
        with open(fname, 'a') as fh:
            if os.stat(fname).st_size == 0:
                fh.write("icrs\n")
            if not rejected:
                fh.write("ellipse({}, {}, {}, {}, {}) # text={{{}}}\n".format(
                    x_cen.value, y_cen.value, major_fwhm.value,
                    minor_fwhm.value, position_angle.value, i))
        pb.update()

    # Plot the grid of sources
    plot_grid(data_cube, masks, rejects, snr_vals, catalog['_idx'])
    plt.suptitle(
        'region={}, band={}, min_value={}, min_delta={}, min_npix={}, threshold={:.4f}'
        .format(region, band, min_value, min_delta, min_npix, threshold))
    plt.show(block=False)

    # Get overrides from user
    print(
        'Manual overrides example: type "r319, a605" to manually reject source #319 and accept source #605.'
    )
    overrides = input(
        "\nType manual override list, or press enter to continue:\n").split(
            ', ')
    accepted_list = [
        s[1:] for s in list(filter(lambda x: x.startswith('a'), overrides))
    ]
    rejected_list = [
        s[1:] for s in list(filter(lambda x: x.startswith('r'), overrides))
    ]

    # Save the manually accepted and rejected sources
    fname = './.override/accept_' + outfile + '.txt'
    with open(fname, 'a') as fh:
        for num in accepted_list:
            fh.write('\n' + str(num))
    fname = './.override/reject_' + outfile + '.txt'
    with open(fname, 'a') as fh:
        for num in rejected_list:
            fh.write('\n' + str(num))
    print(
        "Manual overrides written to './.override/' and saved to source catalog. New overrides will be displayed the next time the rejection script is run."
    )

    # Process the new overrides, to be saved into the catalog
    rejects = np.array(rejects)
    acc = np.array([a[-2:] for a in accepted_list], dtype=int)
    rej = np.array([r[-2:] for r in rejected_list], dtype=int)
    rejects[acc] = 0
    rejects[rej] = 1

    # Save the catalog with new columns for SNR
    catalog.add_column(Column(snr_vals), name='snr_band' + band)
    catalog.add_column(np.invert(catalog.mask['snr_band' + band]).astype(int),
                       name='detected_band' + band)
    catalog.add_column(Column(rejects), name='rejected')
    catalog.write('./cat/cat_' + outfile + '_filtered.dat', format='ascii')
def fit_source(srcID,
               img,
               img_name,
               band,
               fit_bg=False,
               bg_stddev_x=30,
               bg_stddev_y=30,
               bg_mean_x=0,
               bg_mean_y=0,
               zoom=1,
               max_offset_in_beams=1,
               max_radius_in_beams=5,
               nonconv_img=None,
               mask_size=1.5):
    #this function fits a given source, and the background
    #srcID : int
    #name of source to fit in catalogs
    #img : fits file
    #fits file with source to fit
    #img_name : str
    #name of image for the directory where the fit plots will go
    #band : str
    #band of image to fit ('B3', 'B6', or 'B7')
    #fit_bg : bool
    #if False, do not fit background gaussian
    #bg_stddev_x : float
    #eyeballed estimate of stddev of the background source in pixels
    #bg_stddev_y : float
    #same as above in y direction
    #bg_mean_x/y : float
    #pixels away from center (origin) in x/y direction for background gaussian mean guess
    #zoom : float
    #amount of zoom, values greater than 1 are zoom ins

    #ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/dendro_ref_catalog_edited.fits'
    ref_data_name = '/home/jotter/nrao/summer_research_2018/tables/ref_catalog_may21_b7.fits'
    #ref_data_name = '/lustre/cv/observers/cv-12578/orion_disks/summer_research_2018/tables/ref_catalog_may21.fits'
    ref_data = Table.read(ref_data_name)

    fl = fits.open(img)
    header = fl[0].header
    img_data = fl[0].data.squeeze()
    img_wcs = WCS(header).celestial

    beam = radio_beam.Beam.from_fits_header(header)
    pixel_scale = np.abs(
        img_wcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
    ppbeam = (beam.sr / (pixel_scale**2)).decompose().value

    if nonconv_img is not None:
        flnonconv = fits.open(nonconv_img)
        nonconv_header = flnonconv[0].header
        nonconv_beam = radio_beam.Beam.from_fits_header(nonconv_header)
        ppbeam = (nonconv_beam.sr / (pixel_scale**2)).decompose().value

    #now get ready to fit gaussians
    #start by setting up save directory for images
    gauss_save_dir = '/home/jotter/nrao/gauss_diags_may21/fitbg/' + img_name + '/'
    #gauss_save_dir = f'/lustre/cv/observers/cv-12578/orion_disks/gauss_diags_may21/{img_name}/'

    print('saving plots to ' + gauss_save_dir)
    if not os.path.exists(gauss_save_dir):
        os.makedirs(gauss_save_dir)
    #now make region
    rad = Angle(1, 'arcsecond')  #radius used in region list
    #src_ind = np.where(ref_data['D_ID']==srcID)[0]
    src_ind = np.where(ref_data['B3_Seq'] == srcID)[0]

    ra = ref_data['RA_B3'][src_ind].data[0]
    dec = ref_data['DEC_B3'][src_ind].data[0]
    center_reg = SkyCoord(ra, dec, unit='deg', frame='icrs')
    reg = regions.CircleSkyRegion(
        center=center_reg,
        radius=1 * u.arcsecond,
        meta={
            'text':
            str(ref_data['B3_Seq'][src_ind].data[0]) + '_xstddev_' +
            str(bg_stddev_x) + '_ystddev_' + str(bg_stddev_y)
        })

    region_list = []
    #valid_inds = np.where(np.isnan(ref_data[band+'_detect']) == False)[0]
    for ind in range(len(ref_data)):  #valid_inds:
        if ref_data['B3_Seq'][ind] == srcID:
            continue
        ra_i = ref_data['RA_B3'][ind]
        dec_i = ref_data['DEC_B3'][ind]
        region_i = regions.CircleSkyRegion(center=SkyCoord(ra_i,
                                                           dec_i,
                                                           unit='deg',
                                                           frame='icrs'),
                                           radius=1 * u.arcsecond)
        region_list.append(region_i)

    #print(region_list)
    #print(reg)

    cat_r = Angle(0.5, 'arcsecond') / zoom  #radius for gaussian fitting

    if fit_bg == True:
        gauss_cat, fitim_bg = bg_gaussfit(
            img,
            reg,
            region_list,
            cat_r,
            bg_stddev_x=bg_stddev_x,
            bg_stddev_y=bg_stddev_y,
            bg_mean_x=bg_mean_x,
            bg_mean_y=bg_mean_y,
            savepath=gauss_save_dir,
            max_offset_in_beams=max_offset_in_beams,
            max_offset_in_beams_bg=10,
            max_radius_in_beams=max_radius_in_beams,
            mask_size=mask_size)

        #print('gauss_cat length ',len(gauss_cat))
        #k = list(gauss_cat.keys())[0]
        #if gauss_cat[k]['success'] == False:
        #    gauss_cat = gaussfit_cutoutim(img, fitim_bg, reg, region_list, cat_r, savepath=gauss_save_dir, max_offset_in_beams = max_offset_in_beams, max_radius_in_beams = max_radius_in_beams)
        #    success = gauss_cat[k]['success']
        #    print(F'ALTERNATIVE FIT SUCCESS: {success}')

    else:
        gauss_cat = gaussfit_catalog(img, [reg],
                                     cat_r,
                                     savepath=gauss_save_dir,
                                     max_offset_in_beams=max_offset_in_beams,
                                     max_radius_in_beams=max_radius_in_beams)

    img_table = Table(names=('Seq', 'fwhm_maj_' + band, 'fwhm_maj_err_' + band,
                             'fwhm_min_' + band, 'fwhm_min_err_' + band,
                             'pa_' + band, 'pa_err_' + band,
                             'gauss_amp_' + band, 'gauss_amp_err_' + band,
                             'RA_' + band, 'RA_err_' + band, 'DEC_' + band,
                             'DEC_err_' + band),
                      dtype=('i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8',
                             'f8', 'f8', 'f8', 'f8', 'f8'))
    for key in gauss_cat:
        img_table.add_row(
            (srcID, gauss_cat[key]['fwhm_major'],
             gauss_cat[key]['e_fwhm_major'], gauss_cat[key]['fwhm_minor'],
             gauss_cat[key]['e_fwhm_minor'], gauss_cat[key]['pa'],
             gauss_cat[key]['e_pa'], gauss_cat[key]['amplitude'],
             gauss_cat[key]['e_amplitude'], gauss_cat[key]['center_x'],
             gauss_cat[key]['e_center_x'], gauss_cat[key]['center_y'],
             gauss_cat[key]['e_center_y']))

    #now measure deconvovled sizes and aperture flux measurements for each source
    ap_flux_arr = []
    ap_flux_err_arr = []
    fwhm_maj_deconv_arr = []
    fwhm_maj_deconv_err_arr = []
    fwhm_min_deconv_arr = []
    fwhm_min_deconv_err_arr = []
    pa_deconv_arr = []
    pa_deconv_err_arr = []
    snr_arr = []

    for row in range(
            len(img_table)
    ):  #now loop through sources in reference data and make measurements
        ref_ind = np.where(ref_data['B3_Seq'] == img_table['Seq'][row])[0]
        if True == True:  #len(ref_ind > 0):

            measured_source_size = radio_beam.Beam(
                major=img_table['fwhm_maj_' + band][row] * u.arcsec,
                minor=img_table['fwhm_min_' + band][row] * u.arcsec,
                pa=(img_table['pa_' + band][row] - 90) * u.deg)
            try:
                deconv_size = measured_source_size.deconvolve(beam)
                fwhm_maj_deconv_arr.append(deconv_size.major.value)
                fwhm_min_deconv_arr.append(deconv_size.minor.value)
                fwhm_maj_deconv_err_arr.append(img_table['fwhm_maj_err_' +
                                                         band][row])
                fwhm_min_deconv_err_arr.append(img_table['fwhm_min_err_' +
                                                         band][row])
                pa_deconv_arr.append(deconv_size.pa.value)
                pa_deconv_err_arr.append(img_table['pa_err_' + band][row])

            except ValueError:
                fwhm_maj_deconv_arr.append(np.nan)
                fwhm_min_deconv_arr.append(np.nan)
                fwhm_maj_deconv_err_arr.append(np.nan)
                fwhm_min_deconv_err_arr.append(np.nan)
                pa_deconv_arr.append(np.nan)
                pa_deconv_err_arr.append(np.nan)

            pix_major_fwhm = (
                (img_table['fwhm_maj_' + band][row] * u.arcsec).to(u.degree) /
                pixel_scale).decompose()
            pix_minor_fwhm = (
                (img_table['fwhm_min_' + band][row] * u.arcsec).to(u.degree) /
                pixel_scale).decompose()
            center_coord = SkyCoord(img_table['RA_' + band][row],
                                    img_table['DEC_' + band][row],
                                    frame='icrs',
                                    unit=(u.deg, u.deg))
            center_coord_pix = center_coord.to_pixel(img_wcs)
            center_coord_pix_reg = regions.PixCoord(center_coord_pix[0],
                                                    center_coord_pix[1])

            pos_ang = (img_table['pa_' + band][row] - 90) * u.deg

            ellipse_reg = regions.EllipsePixelRegion(center_coord_pix_reg,
                                                     pix_major_fwhm.value * 2,
                                                     pix_minor_fwhm.value * 2,
                                                     angle=pos_ang)
            size = pix_major_fwhm * 2.1
            ap_mask = ellipse_reg.to_mask()
            cutout_mask = ap_mask.cutout(img_data)

            aperture_flux = np.nansum(cutout_mask[ap_mask.data == 1]) / ppbeam
            npix = len(cutout_mask[ap_mask.data == 1])

            #now make annulus for measuring background and error
            annulus_width = 15  #pixels
            annulus_radius = img_table[
                'fwhm_maj_' + band][row] * u.arcsecond  #0.1*u.arcsecond
            annulus_radius_pix = (annulus_radius.to(u.degree) /
                                  pixel_scale).decompose()

            #cutout image
            cutout = Cutout2D(img_data,
                              center_coord_pix,
                              annulus_radius * 2.5,
                              img_wcs,
                              mode='partial')
            cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                             cutout.center_cutout[1])

            #define aperture regions for SNR
            innerann_reg = regions.CirclePixelRegion(cutout_center,
                                                     annulus_radius_pix.value)
            outerann_reg = regions.CirclePixelRegion(
                cutout_center, annulus_radius_pix.value + annulus_width)

            #Make masks from aperture regions
            annulus_mask = mask(outerann_reg, cutout) - mask(
                innerann_reg, cutout)

            # Calculate the SNR and aperture flux sums
            pixels_in_annulus = cutout.data[annulus_mask.astype(
                'bool')]  #pixels within annulus
            bg_rms = median_abs_deviation(pixels_in_annulus)
            ap_bg_rms = bg_rms / np.sqrt(
                npix / ppbeam)  #rms/sqrt(npix/ppbeam) - rms error per beam
            bg_median = np.median(pixels_in_annulus)

            pix_bg = bg_median * npix / ppbeam

            #flux corrections
            ap_flux_bgcorrect = aperture_flux - pix_bg
            ap_flux_correct = ap_flux_bgcorrect + ap_flux_bgcorrect * (
                1 - special.erf(2 * np.sqrt(np.log(2)))
            )  #flux correction for summing within 2*fwhm

            print(
                f'Background: {pix_bg}, Gauss amp: {img_table["gauss_amp_"+band][row]}'
            )
            print(f'peak pixel: {np.nanmax(cutout_mask[ap_mask.data==1])}')

            ap_flux_err_arr.append(ap_bg_rms)
            ap_flux_arr.append(ap_flux_correct)
            snr_arr.append(img_table['gauss_amp_' + band][row] / bg_rms)

    cols = [
        'ap_flux_' + band, 'ap_flux_err_' + band, 'fwhm_maj_deconv_' + band,
        'fwhm_maj_deconv_err_' + band, 'fwhm_min_deconv_' + band,
        'fwhm_min_deconv_err_' + band, 'pa_deconv_' + band,
        'pa_deconv_err_' + band, 'SNR_' + band
    ]
    arrs = [
        ap_flux_arr, ap_flux_err_arr, fwhm_maj_deconv_arr,
        fwhm_maj_deconv_err_arr, fwhm_min_deconv_arr, fwhm_min_deconv_err_arr,
        pa_deconv_arr, pa_deconv_err_arr, snr_arr
    ]
    for c in range(len(cols)):
        img_table.add_column(Column(np.array(arrs[c])), name=cols[c])
    img_table.add_column(Column(img_table['fwhm_maj_deconv_' + band] /
                                img_table['fwhm_min_deconv_' + band]),
                         name='ar_deconv_' + band)

    return img_table
Beispiel #11
0
def flux(region):

    # import the catalog file, get names of bands
    filename = glob('./cat/mastercat_region{}*'.format(region))[0]
    catalog = Table(Table.read(filename, format='ascii'), masked=True)
    catalog.sort('_idx')

    bands = np.array(filename.split('bands_')[1].split('.dat')[0].split('_'),
                     dtype=int)
    n_bands = len(bands)
    n_rows = len(catalog)

    ellipse_npix_col = MaskedColumn(length=len(catalog),
                                    name='ellipse_npix',
                                    mask=True)
    circ1_npix_col = MaskedColumn(length=len(catalog),
                                  name='circ1_npix',
                                  mask=True)
    circ2_npix_col = MaskedColumn(length=len(catalog),
                                  name='circ2_npix',
                                  mask=True)
    circ3_npix_col = MaskedColumn(length=len(catalog),
                                  name='circ3_npix',
                                  mask=True)

    n_rejected = 0

    for i in range(n_bands):

        band = bands[i]

        # Load image file for this band
        print("\nLoading image file for region {} in band {} (Image {}/{})".
              format(region, band, i + 1, n_bands))
        imfile = grabfileinfo(region, band)[0]
        contfile = fits.open(imfile)
        data = contfile[0].data.squeeze()

        # Set up wcs, beam, and pixel scale for this image
        mywcs = wcs.WCS(contfile[0].header).celestial
        beam = radio_beam.Beam.from_fits_header(contfile[0].header)
        pixel_scale = np.abs(
            mywcs.pixel_scale_matrix.diagonal().prod())**0.5 * u.deg
        ppbeam = (beam.sr / (pixel_scale**2)).decompose().value
        print('ppbeam: ', ppbeam)
        data = data / ppbeam

        # Set up columns for each aperture
        peak_flux_col = MaskedColumn(length=len(catalog),
                                     name='peak_flux_band{}'.format(band),
                                     mask=True)
        annulus_median_col = MaskedColumn(
            length=len(catalog),
            name='annulus_median_band{}'.format(band),
            mask=True)
        annulus_rms_col = MaskedColumn(length=len(catalog),
                                       name='annulus_rms_band{}'.format(band),
                                       mask=True)
        ellipse_flux_col = MaskedColumn(
            length=len(catalog),
            name='ellipse_flux_band{}'.format(band),
            mask=True)
        circ1_flux_col = MaskedColumn(length=len(catalog),
                                      name='circ1_flux_band{}'.format(band),
                                      mask=True)
        circ2_flux_col = MaskedColumn(length=len(catalog),
                                      name='circ2_flux_band{}'.format(band),
                                      mask=True)
        circ3_flux_col = MaskedColumn(length=len(catalog),
                                      name='circ3_flux_band{}'.format(band),
                                      mask=True)

        ellipse_rms_col = MaskedColumn(length=len(catalog),
                                       name='ellipse_rms_band{}'.format(band),
                                       mask=True)
        circ1_rms_col = MaskedColumn(length=len(catalog),
                                     name='circ1_rms_band{}'.format(band),
                                     mask=True)
        circ2_rms_col = MaskedColumn(length=len(catalog),
                                     name='circ2_rms_band{}'.format(band),
                                     mask=True)
        circ3_rms_col = MaskedColumn(length=len(catalog),
                                     name='circ3_rms_band{}'.format(band),
                                     mask=True)

        circ1_r, circ2_r, circ3_r = 5e-6 * u.deg, 1e-5 * u.deg, 1.5e-5 * u.deg

        print('Photometering sources')
        pb = ProgressBar(len(catalog[np.where(catalog['rejected'] == 0)]))

        masks = []
        datacube = []
        rejects = []
        snr_vals = []
        names = []

        # Iterate over sources, extracting ellipse parameters
        for j in range(n_rows):

            if catalog['rejected'][j] == 1:
                continue

            source = catalog[j]
            x_cen = source['x_cen'] * u.deg
            y_cen = source['y_cen'] * u.deg
            major = source['major_fwhm'] * u.deg
            minor = source['minor_fwhm'] * u.deg
            pa = source['position_angle'] * u.deg

            annulus_width = 1e-5 * u.deg
            center_distance = 1e-5 * u.deg

            # Convert to pixel coordinates
            position = coordinates.SkyCoord(x_cen,
                                            y_cen,
                                            frame='icrs',
                                            unit=(u.deg, u.deg))
            pix_position = np.array(position.to_pixel(mywcs))
            pix_major = major / pixel_scale
            pix_minor = minor / pixel_scale

            # Create cutout
            size = np.max([
                circ3_r.value,
                major.value + center_distance.value + annulus_width.value
            ]) * 2.2 * u.deg
            try:
                cutout = Cutout2D(data, position, size, mywcs, mode='partial')
            except NoOverlapError:
                catalog['rejected'][j] = 1
                pb.update()
                continue
            cutout_center = regions.PixCoord(cutout.center_cutout[0],
                                             cutout.center_cutout[1])
            datacube.append(cutout.data)

            # create all aperture shapes
            ellipse_reg = regions.EllipsePixelRegion(cutout_center,
                                                     pix_major * 2.,
                                                     pix_minor * 2.,
                                                     angle=pa)
            circ1_reg = regions.CirclePixelRegion(cutout_center,
                                                  circ1_r / pixel_scale)
            circ2_reg = regions.CirclePixelRegion(cutout_center,
                                                  circ2_r / pixel_scale)
            circ3_reg = regions.CirclePixelRegion(cutout_center,
                                                  circ3_r / pixel_scale)

            innerann_reg = regions.CirclePixelRegion(
                cutout_center, center_distance / pixel_scale + pix_major)
            outerann_reg = regions.CirclePixelRegion(
                cutout_center, center_distance / pixel_scale + pix_major +
                annulus_width / pixel_scale)

            annulus_mask = mask(outerann_reg, cutout) - mask(
                innerann_reg, cutout)

            # get flux information from regions
            ellipse_flux, ellipse_rms, peak_flux, ellipse_mask, ellipse_npix = apsum(
                ellipse_reg, cutout)
            circ1_flux, circ1_rms, _, circ1_mask, circ1_npix = apsum(
                circ1_reg, cutout)
            circ2_flux, circ2_rms, _, circ2_mask, circ2_npix = apsum(
                circ2_reg, cutout)
            circ3_flux, circ3_rms, _, circ3_mask, circ3_npix = apsum(
                circ3_reg, cutout)

            annulus_rms = rms(cutout.data[annulus_mask.astype('bool')])
            annulus_median = np.median(
                cutout.data[annulus_mask.astype('bool')])

            # Add grid plot mask to list
            masklist = [
                ellipse_mask, annulus_mask, circ1_mask, circ2_mask, circ3_mask
            ]
            masks.append(masklist)

            # add fluxes to appropriate columns
            peak_flux_col[j] = peak_flux
            ellipse_flux_col[j], ellipse_rms_col[j] = ellipse_flux, ellipse_rms
            circ1_flux_col[j], circ1_rms_col[j] = circ1_flux, circ1_rms
            circ2_flux_col[j], circ2_rms_col[j] = circ2_flux, circ2_rms
            circ3_flux_col[j], circ3_rms_col[j] = circ3_flux, circ3_rms

            ellipse_npix_col[j] = ellipse_npix
            circ1_npix_col[j] = circ1_npix
            circ2_npix_col[j] = circ2_npix
            circ3_npix_col[j] = circ3_npix

            annulus_median_col[j] = annulus_median
            annulus_rms_col[j] = annulus_rms

            catalog['snr_band' + str(band)][j] = peak_flux / annulus_rms
            snr_vals.append(peak_flux / annulus_rms)
            names.append(catalog['_idx'][j])

            # Secondary rejection
            rejected = 0
            lowest_flux = np.min(
                [ellipse_flux, circ1_flux, circ2_flux, circ3_flux])
            #if lowest_flux <= annulus_median*ellipse_npix or lowest_flux < 0:
            if lowest_flux < 0:
                catalog['rejected'][j] = 1
                n_rejected += 1
                rejected = 1
            rejects.append(rejected)
            pb.update()

        # Plot the grid of sources
        plot_grid(datacube, masks, rejects, snr_vals, names)
        plt.suptitle('region={}, band={}'.format(region, band))
        plt.show(block=False)

        # add columns to catalog
        catalog.add_columns([
            peak_flux_col,
            ellipse_flux_col,
            ellipse_rms_col,
            circ1_flux_col,
            circ1_rms_col,
            circ2_flux_col,
            circ2_rms_col,
            circ3_flux_col,
            circ3_rms_col,
        ])

    catalog.add_columns([
        ellipse_npix_col, circ1_npix_col, circ2_npix_col, circ3_npix_col,
        annulus_median_col, annulus_rms_col
    ])
    print("\n{} sources flagged for secondary rejection".format(n_rejected))

    # save catalog
    catalog = catalog[sorted(catalog.colnames)]
    catalog.write(filename.split('.dat')[0] + '_photometered.dat',
                  format='ascii')
    print("\nMaster catalog saved as '{}'".format(
        filename.split('.dat')[0] + '_photometered.dat'))
Beispiel #12
0
def ginga_canvas_object_to_astropy_region(obj):
    """
    Convert a Ginga canvas object to an AstroPy region object.

    Parameters
    ----------
    obj : subclass of `~ginga.canvas.CanvasObject`
        The Ginga canvas object to be converted

    Returns
    -------
    r : subclass of `~regions.PixelRegion`
        The corresponding AstroPy region object

    """
    if not HAVE_REGIONS:
        raise ValueError(
            "Please install the Astropy 'regions' package to use this function"
        )

    dc = get_canvas_types()
    r = None

    if isinstance(obj, (dc.Circle, )):
        r = regions.CirclePixelRegion(center=regions.PixCoord(x=obj.x,
                                                              y=obj.y),
                                      radius=obj.radius)

    elif isinstance(obj, (dc.Ellipse, )):
        r = regions.EllipsePixelRegion(center=regions.PixCoord(x=obj.x,
                                                               y=obj.y),
                                       width=obj.xradius * 2,
                                       height=obj.yradius * 2,
                                       angle=obj.rot_deg * u.deg)

    elif isinstance(obj, (dc.Text, )):
        r = regions.TextPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y),
                                    text=obj.text)
        r.visual['textangle'] = str(obj.rot_deg)

    elif isinstance(obj, (dc.Point, )):
        r = regions.PointPixelRegion(center=regions.PixCoord(x=obj.x, y=obj.y))
        style = pt_ginga.get(obj.style, '*')
        r.visual['symbol'] = style

    elif isinstance(obj, (dc.Line, )):
        r = regions.LinePixelRegion(start=regions.PixCoord(x=obj.x1, y=obj.y1),
                                    end=regions.PixCoord(x=obj.x2, y=obj.y2))

    elif isinstance(obj, (dc.Box, )):
        r = regions.RectanglePixelRegion(center=regions.PixCoord(x=obj.x,
                                                                 y=obj.y),
                                         width=obj.xradius * 2,
                                         height=obj.yradius * 2,
                                         angle=obj.rot_deg * u.deg)

    elif isinstance(obj, (dc.Polygon, )):
        x, y = np.asarray(obj.points).T
        r = regions.PolygonPixelRegion(vertices=regions.PixCoord(x=x, y=y))

    elif isinstance(obj, (dc.Annulus, )) and obj.atype == 'circle':
        rin = obj.radius
        rout = rin + obj.width
        r = regions.CircleAnnulusPixelRegion(center=regions.PixCoord(x=obj.x,
                                                                     y=obj.y),
                                             inner_radius=rin,
                                             outer_radius=rout)

    elif isinstance(obj, (dc.Annulus2R, )) and obj.atype == 'ellipse':
        r = regions.EllipseAnnulusPixelRegion(
            center=regions.PixCoord(x=obj.x, y=obj.y),
            inner_width=obj.xradius * 2,
            inner_height=obj.yradius * 2,
            outer_width=obj.xradius * 2 + obj.xwidth * 2,
            outer_height=obj.yradius * 2 + obj.ywidth * 2,
            angle=obj.rot_deg * u.deg)

    elif isinstance(obj, (dc.Annulus2R, )) and obj.atype == 'box':
        r = regions.RectangleAnnulusPixelRegion(
            center=regions.PixCoord(x=obj.x, y=obj.y),
            inner_width=obj.xradius * 2,
            inner_height=obj.yradius * 2,
            outer_width=obj.xradius * 2 + obj.xwidth * 2,
            outer_height=obj.yradius * 2 + obj.ywidth * 2,
            angle=obj.rot_deg * u.deg)

    else:
        raise ValueError("Don't know how to convert this object")

    # Set visual styling attributes
    r.visual['color'] = obj.color

    if hasattr(obj, 'font'):
        r.visual['font'] = obj.font
        if obj.fontsize is not None:
            r.visual['fontsize'] = str(obj.fontsize)

    if hasattr(obj, 'linewidth'):
        r.visual['linewidth'] = obj.linewidth

    if hasattr(obj, 'fill'):
        r.visual['fill'] = obj.fill = r.visual.get('fill', False)

    # Limited support for other metadata
    r.meta['edit'] = 1 if obj.editable else 0
    meta = obj.get_data()
    if meta is not None and meta.get('name', None) is not None:
        r.meta['name'] = meta.get('name')

    return r
def reject_sources(name,
                   catname,
                   datname,
                   min_snr=5,
                   max_size=None,
                   max_size_ID=None,
                   flux_type='peak'):

    #catname: name of catalog of sources, with required columns 'gauss_x_'+name, 'gauss_y_'+name, 'FWHM_major_'+name, 'FWHM_minor_'+name, and 'position_angle_'+name
    #datname: name of data fits image
    #min_snr: minimum SNR value, all sources with SNR below this will be rejected
    #max_size: maximum major axis radius for ellipse around source, in sigma
    #max_size_ID: alternatively, give the index of a source to set that source's radius to the maximum
    #flux_type: if 'peak', chooses the brightest pixel, if 'percentile', flux measured by average of top 10% of pixels

    catalog = fits.getdata(catname)
    catalog = Table(catalog)

    bad_inds = np.where(np.isnan(catalog['ap_flux_' + name]) == True)
    catalog.remove_rows(bad_inds)

    fl = fits.open(datname)
    data = fl[0].data.squeeze()
    header = fl[0].header
    mywcs = WCS(header).celestial

    sigma_to_FWHM = 2 * np.sqrt(2 * np.log(2))

    pixel_scale = np.abs(mywcs.pixel_scale_matrix.diagonal().prod()
                         )**0.5 * u.deg  #for conversion to pixels

    snr_vals = []
    cutout_images = []
    masks = []
    bg_arr = []
    bg_arr2 = []
    reject = np.full(len(catalog), False)

    for i in range(len(catalog)):
        x_cen = catalog['gauss_x_' + name][i] * u.deg
        y_cen = catalog['gauss_y_' + name][i] * u.deg
        major_fwhm = (catalog['FWHM_major_' + name][i] * u.arcsec).to(u.degree)
        minor_fwhm = (catalog['FWHM_minor_' + name][i] * u.arcsec).to(u.degree)
        position_angle = catalog['position_angle_' + name][i] * u.deg

        annulus_width = 15
        center_pad = 10  #pad between ellipse and inner radius
        # Define some ellipse properties in pixel coordinates
        position = SkyCoord(x_cen, y_cen, frame='icrs', unit=(u.deg, u.deg))
        pix_position = np.array(position.to_pixel(mywcs))

        pix_major_fwhm = major_fwhm / pixel_scale
        pix_minor_fwhm = minor_fwhm / pixel_scale

        # Cutout section of the image we care about, to speed up computation time
        size = (
            (center_pad + annulus_width) * pixel_scale + major_fwhm
        ) * 2.2  #2.2 is arbitrary to get entire annulus and a little extra
        cutout = Cutout2D(data, position, size, mywcs,
                          mode='partial')  #cutout of outer circle
        cutout_center = regions.PixCoord(
            cutout.center_cutout[0],
            cutout.center_cutout[1])  #center of the cutout in pixel coords

        # Define the aperture regions needed for SNR
        ellipse_reg = regions.EllipsePixelRegion(cutout_center,
                                                 pix_major_fwhm * 2.,
                                                 pix_minor_fwhm * 2.,
                                                 angle=position_angle)
        innerann_reg = regions.CirclePixelRegion(cutout_center,
                                                 center_pad + pix_major_fwhm)
        outerann_reg = regions.CirclePixelRegion(
            cutout_center, center_pad + pix_major_fwhm + annulus_width)

        # Make masks from aperture regions
        ellipse_mask = mask(ellipse_reg, cutout)
        annulus_mask = mask(outerann_reg, cutout) - mask(innerann_reg, cutout)

        # Calculate the SNR and aperture flux sums
        pixels_in_annulus = cutout.data[annulus_mask.astype(
            'bool')]  #pixels within annulus
        pixels_in_ellipse = cutout.data[ellipse_mask.astype(
            'bool')]  #pixels in ellipse
        bg_rms = rms(pixels_in_annulus)
        bg_mean = np.mean(pixels_in_annulus)
        bg_median = np.median(pixels_in_annulus)

        if flux_type == 'peak':
            peak_flux = catalog['peak_flux_' + name][i]
        if flux_type == 'percentile':
            top_percent = np.nanpercentile(pixels_in_ellipse, 90)
            peak_flux = np.mean(
                pixels_in_ellipse[pixels_in_ellipse > top_percent])
        snr = peak_flux / bg_rms
        catalog['ap_flux_err_' + name][i] = bg_rms
        bg_arr.append(bg_mean)
        bg_arr2.append(bg_median)

        if snr < min_snr:  #if low snr, reject
            reject[i] = True
        if max_size_ID is not None:
            if catalog['major_sigma'][i] > catalog['major_sigma'][
                    max_size_ID] + 0.01 / 3600:  #if too big a source, reject
                reject[i] = True
        if max_size is not None:
            if catalog['major_sigma'][
                    i] > max_size:  #if too big a source, reject
                reject[i] = True
        snr_vals.append(snr)
        cutout_images.append(cutout.data)
        masks.append(ellipse_mask + annulus_mask)

    catalog['bg_mean_' + name] = bg_arr
    catalog['bg_median_' + name] = bg_arr2
    plot_grid(cutout_images, masks, reject, snr_vals, catalog['_idx_' + name])
    plt.show(block=False)
    line_remove = input(
        'enter id values for sources to exclude from the catalog, seperated by whitespace: '
    )
    man_input_rem = np.array(line_remove.split(), dtype='int')
    id_ind_true = np.where(
        np.in1d(catalog['_idx_' + name], man_input_rem) == True)
    reject[id_ind_true] = True

    line_keep = input(
        'enter id values for removed sources to include from the catalog, seperated by whitespace: '
    )
    man_input_keep = np.array(line_keep.split(), dtype='int')
    id_ind_false = np.where(
        np.in1d(catalog['_idx_' + name], man_input_keep) == True)
    reject[id_ind_false] = False

    rej_ind = np.where(reject == True)
    catalog.remove_rows(rej_ind)
    return catalog