def voronoi_binning(image, obj_name, targetSN = 50, largest_bin = 5, smallest_bin = 0, minimumSN = 7, quiet=True, plot=True): """ Function to bin an image using the Voronoi binning method by Cappellari & Copin (2003) Input as 'image' the target with the filter where S/N is highest """ import numpy as np import astropy.io.fits as pyfits from grizli import utils from grizli import prep import matplotlib.pyplot as plt from astropy.table import vstack im = pyfits.open(image) sci = np.cast[np.float32](im['SCI'].data) sh = sci.shape ivar = np.cast[np.float32](im['WHT'].data) var = 1/ivar orig_mask = (ivar > 0) sci[~orig_mask] = 0 var[~orig_mask] = 0 orig_var = var*1 # Simple background bkg = np.median(sci[orig_mask]) sci -= bkg*orig_mask cps = sci*im[0].header['EXPTIME'] shot_err = np.sqrt(np.maximum(cps, 4))/im[0].header['EXPTIME'] var2 = var + shot_err**2 var = var2 full_bin_seg = np.zeros(sh, dtype=np.int)-1 yp, xp = np.indices(sci.shape) xpf = xp.flatten() ypf = yp.flatten() # Initialize mask mask = orig_mask & True bin_min = 1 full_image = sci*0. full_err = sci*0. idx = np.arange(sci.size, dtype=np.int) full_image = full_image.flatten() full_err = full_err.flatten() # id, bin, xmin, xmax, ymin, ymax, npix full_bin_data = [] SKIP_LAST = False bin_iter = 0 bin_factor = largest_bin NO_NEWLINE = '\x1b[1A\x1b[1M' for bin_iter, bin_factor in enumerate(range(largest_bin+1)[::-1]): bin = 2**bin_factor if bin_factor < smallest_bin: break if (bin_factor == 0) & SKIP_LAST: continue ypb = yp[mask] // bin xpb = xp[mask] // bin if bin_factor > 0: binned_sci = np.zeros((sh[0]//bin+1, sh[1]//bin+1)) binned_npix = binned_sci*0 binned_var = binned_sci*0 ypi, xpi = np.indices(binned_sci.shape) # Only consider unmasked bins ij = np.unique(xpb + sh[0]//bin*ypb) yarr = ij // (sh[0]//bin) xarr = ij - (sh[0]//bin)*yarr for xi, yi in zip(xarr, yarr): if not quiet: print(NO_NEWLINE+'{0} {1}/{2} {3}/{4}'.format(bin_factor, xi, xarr.max(), yi, yarr.max())) slx = slice(xi*bin, xi*bin+bin) sly = slice(yi*bin, yi*bin+bin) mslice = mask[sly, slx] # Straight average binned_sci[yi, xi] = sci[sly, slx][mslice].sum() binned_npix[yi, xi] = mslice.sum() binned_var[yi, xi] = var[sly, slx][mslice].sum() binned_err = np.sqrt(binned_var) / binned_npix binned_avg = binned_sci / binned_npix mask_i = (binned_npix > 0) & (binned_avg/binned_err > minimumSN) xpi = xpi[mask_i] ypi = ypi[mask_i] binned_avg = binned_avg[mask_i] binned_err = binned_err[mask_i] binned_npix = binned_npix[mask_i] else: mask_i = mask_j xpi = xp[mask] ypi = yp[mask] binned_avg = sci[mask] binned_err = np.sqrt(var)[mask] binned_npix = mask[mask]*1 if True: # Mask pixels in that don't satisfy S/N cutoff as they are # unreliable for vorbin clip_mask = mask < 0 for xi, yi in zip(xpi, ypi): slx = slice(xi*bin, xi*bin+bin) sly = slice(yi*bin, yi*bin+bin) clip_mask[sly, slx] = True mask &= clip_mask # Identify blobs (usually the main central galaxies) and # only consider blobs larger than 20% of the largest blob if bin_factor == largest_bin: label_image = label(mask) label_ids = np.unique(label_image)[1:] label_sizes = np.array([(label_image == id_i).sum() for id_i in label_ids]) keep_ids = label_ids[label_sizes > 0.2*label_sizes.max()] keep_mask = mask < -1 for i in keep_ids: keep_mask |= label_image == i mask &= keep_mask # In binned_coords in_blob = keep_mask[ypi*bin, xpi*bin] msg = 'Drop {0} bins not in main blob' print(msg.format((~in_blob).sum())) xpi = xpi[in_blob] ypi = ypi[in_blob] binned_avg = binned_avg[in_blob] binned_err = binned_err[in_blob] binned_npix = binned_npix[in_blob] ypb = yp[mask] // bin xpb = xp[mask] // bin print('Run voronoi_2d_binning, bin_factor={0}'.format(bin_factor)) res = voronoi_2d_binning(xpi, ypi, binned_avg, binned_err, targetSN, quiet=True, plot=False, pixelsize=0.1*bin, cvt=True, wvt=True) binNum, xBin, yBin, xBar, yBar, sn, nPixels, scale = res # Put Voronoi bins with nPixels > 1 back in the original image NBINS = len(nPixels) if (bin_factor == smallest_bin) & (smallest_bin > 0): valid_bins = nPixels > 0 else: valid_bins = nPixels > 1 large_bin_ids = np.arange(NBINS)[valid_bins] # Put bin in original 2D array and store info for b0, bin_id in enumerate(large_bin_ids): m_i = binNum == bin_id xi_bin = xpi[m_i] yi_bin = ypi[m_i] for xi, yi in zip(xi_bin, yi_bin): slx = slice(xi*bin, xi*bin+bin) sly = slice(yi*bin, yi*bin+bin) mslice = mask[sly, slx] full_bin_seg[sly, slx][mslice] = b0+bin_min # Bin properties id_i = b0+bin_min xmin = xi_bin.min()*bin-1 xmax = xi_bin.max()*bin+1 ymin = yi_bin.min()*bin-1 ymax = yi_bin.max()*bin+1 npix = m_i.sum()*bin**2 bin_data_i = [id_i, bin, xmin, xmax, ymin, ymax, npix] full_bin_data.append(bin_data_i) # Update the mask not_in_a_bin = full_bin_seg == -1 mask &= not_in_a_bin bin_min = full_bin_data[-1][0]+1 if not quiet: print('\n\n\n\n\n bin_factor: {0}, bin_min: {1}\n\n\n\n'.format(bin_factor, bin_min)) ## Bin information bin_data = np.array(full_bin_data) # bin_data_i = [id_i, bin, xmin, xmax, ymin, ymax, npix] tab = utils.GTable() for i, c in enumerate(['id', 'bin', 'xmin', 'xmax', 'ymin', 'ymax', 'npix']): tab[c] = bin_data[:,i] if 'min' in c: tab[c] -= tab['bin'] elif 'max' in c: tab[c] += tab['bin'] # Make a table for the individual pixels if mask.sum() > 0: single_table = single_pixel_table(mask,start_id=1+tab['id'].max()) full_bin_seg[mask] = single_table['id'] tab = vstack([tab,single_table]) tab['flux'], tab['err'], tab['area'] = prep.get_seg_iso_flux(sci, full_bin_seg, tab, err=np.sqrt(var)) binned_flux = prep.get_seg_iso_flux(sci, full_bin_seg, tab, fill=tab['flux']/tab['area']) binned_err = prep.get_seg_iso_flux(sci, full_bin_seg, tab, fill=tab['err']/tab['area']) binned_area = prep.get_seg_iso_flux(sci, full_bin_seg, tab, fill=tab['area']) binned_bin = prep.get_seg_iso_flux(sci, full_bin_seg, tab, fill=tab['bin']) binned_flux[mask] = sci[mask] binned_err[mask] = np.sqrt(var)[mask] binned_area[mask] = 1 binned_bin[mask] = 1 if plot: plt.figure(figsize=(12,12)) plt.imshow(binned_flux,vmin=-0.5,vmax=2) # Save output into image fits file primary_extn = pyfits.PrimaryHDU() sci_extn = pyfits.ImageHDU(data=binned_flux.astype(np.float32),name='SCI') err_extn = pyfits.ImageHDU(data=binned_err.astype(np.float32),name='ERR') hdul = pyfits.HDUList([primary_extn, sci_extn, err_extn]) for ext in [0,1]: for k in im[ext].header: if k not in hdul[ext].header: if k in ['COMMENT','HISTORY','']: continue hdul[ext].header[k] = im[ext].header[k] hdul.writeto('binned_{0}_image.fits'.format(obj_name), output_verify='fix', overwrite=True) tab.write('binned_{0}_table.fits'.format(obj_name), overwrite=True) pyfits.writeto('binned_{0}_seg.fits'.format(obj_name),data = full_bin_seg, overwrite=True) pyfits.writeto('binned_{0}_mask.fits'.format(obj_name),data = mask*1, overwrite=True) return tab, full_bin_seg, mask
def eazy_fitting(catalog_file='catalog.fits', target='galaxy', im='image.fits', seg_im='seg.fits', mw_ebv=0.0375, plot=False, image_space=False, zsp=0.004556): """ Function fitting the input target and obtain physical parameters. INPUTS: catalog_file: Catalog file with the fluxes and errors to be fitted. target: The name of the target, to be used in the output files. im: Image to be used as reference to convert output from table space into image space. [optional] seg_im: Segmentation image. [optional] mw_ebv: Galactic extinction. Value can be found here: https://irsa.ipac.caltech.edu/applications/DUST/ KEYWORDS: PLOT: Set this keyword to produce a plot of the two-dimensional continuum subtracted image. IMAGE_SPACE: Set this keyword to produce the output in image space. OUTPUTS: EAZY derived physical parameters. """ import os import numpy as np import matplotlib.pyplot as plt import eazy import warnings from grizli import prep from astropy.utils.exceptions import AstropyWarning import astropy.io.fits as pyfits from astropy.table import Table from astropy.cosmology import WMAP9 from astropy import units as u np.seterr(all='ignore') warnings.simplefilter('ignore', category=AstropyWarning) # Parameters params = {} params['CATALOG_FILE'] = catalog_file params['MAIN_OUTPUT_FILE'] = target + '.eazypy' # Galactic extinction params['MW_EBV'] = mw_ebv params['SYS_ERR'] = 0.05 params['Z_STEP'] = 0.0002 params['Z_MIN'] = np.maximum(zsp - 10 * params['Z_STEP'] * (1 + zsp), 0) params['Z_MAX'] = zsp + 10 * params['Z_STEP'] * (1 + zsp) params['PRIOR_ABZP'] = 23.9 params['PRIOR_FILTER'] = 241 # K params['PRIOR_FILE'] = 'templates/prior_K_TAO.dat' params['TEMPLATES_FILE'] = 'templates/fsps_full/tweak_fsps_QSF_12_v3.param' params['FIX_ZSPEC'] = True #translate_file = 'zphot.translate' translate_file = os.path.join(os.getenv('EAZYCODE'), 'inputs/zphot.translate') self = eazy.photoz.PhotoZ(param_file=None, translate_file=translate_file, zeropoint_file=None, params=params, load_prior=True, load_products=False) # Now fit the whole catalog # Turn off error corrections derived above self.efnu = self.efnu_orig * 1 # Full catalog sample = np.isfinite(self.cat['z_spec']) t = self.cat # sel = (t['xmin']>(x0-size))&(t['ymin']>(y0-size))&(t['xmax']<(x0+size))&(t['ymax']<(y0+size)) # sample = sel self.fit_parallel(self.idx[sample], n_proc=8) # Derived parameters (z params, RF colors, masses, SFR, etc.) zout, hdu = self.standard_output(rf_pad_width=0.5, rf_max_err=2, prior=False, beta_prior=False, extra_rf_filters=[272, 273, 274]) # 'zout' also saved to [MAIN_OUTPUT_FILE].zout.fits if plot: # Show UVJ diagram uv = -2.5 * np.log10(zout['restU'] / zout['restV']) vj = -2.5 * np.log10(zout['restV'] / zout['restJ']) ssfr = zout['SFR'] / zout['mass'] plt.scatter(vj, uv, c=np.log10(ssfr), cmap='Spectral', vmin=-13, vmax=-8, alpha=0.5) plt.colorbar() plt.xlabel(r'$(V-J)_0$') plt.ylabel(r'$(U-V)_0$') t['x'] = (t['xmin'] + t['xmax']) / 2 t['y'] = (t['ymin'] + t['ymax']) / 2 # Av fig = plt.figure(figsize=(10, 8)) plt.scatter(t['x'], t['y'], marker='.', alpha=0.5, c=zout['Av'], cmap='Spectral_r', vmin=0, vmax=3) ax = plt.gca() ax.set_aspect(1) plt.colorbar() # sSFR fig = plt.figure(figsize=(10, 8)) plt.scatter(t['x'], t['y'], marker='.', alpha=0.5, c=np.log10(ssfr), cmap='Spectral', vmin=-12, vmax=-8) ax = plt.gca() ax.set_aspect(1) plt.colorbar() plt.show() if image_space: image = pyfits.open(im) sci = np.cast[np.float32](image['SCI'].data) seg = pyfits.open(seg_im)[0].data tab = Table.read(catalog_file) # Av flux_Av = prep.get_seg_iso_flux(sci, seg, tab, fill=zout['Av']) fig = plt.figure(figsize=(10, 8)) plt.imshow(flux_Av, cmap='Spectral_r', origin='lower') ax = plt.gca() ax.set_aspect(1) plt.colorbar() primary_extn = pyfits.PrimaryHDU() sci_extn = pyfits.ImageHDU(data=flux_Av, name='SCI', header=image[1].header) hdul = pyfits.HDUList([primary_extn, sci_extn]) hdul.writeto('Av_{0}.fits'.format(target), overwrite=True) # ssfr flux_ssfr = prep.get_seg_iso_flux(sci, seg, tab, fill=np.log10(ssfr)) fig = plt.figure(figsize=(10, 8)) plt.imshow(flux_ssfr, cmap='Spectral', origin='lower') ax = plt.gca() ax.set_aspect(1) plt.colorbar() primary_extn = pyfits.PrimaryHDU() sci_extn = pyfits.ImageHDU(data=flux_ssfr, name='SCI', header=image[1].header) hdul = pyfits.HDUList([primary_extn, sci_extn]) hdul.writeto('sSFR_{0}.fits'.format(target), overwrite=True) plt.show() return zout
def bin_image(im, tab_in, seg_in, mask_in, bkg_in=None, bg_mask_in=None): """ Apply 2D bins specified in "seg_in" to a new image """ from grizli import utils from grizli import prep import numpy as np sci_data = im['SCI'].data*1 var_data = 1/im['WHT'].data wht_mask = im['WHT'].data > 0 IS_ACS = sci_data.shape[0] == 2*seg_in.shape[0] if IS_ACS: # ACS to_flam = im[1].header['PHOTFLAM'] to_fnu = 1. #im[1].header['PHOTFNU'] tab = utils.GTable() for c in tab_in.colnames: if c[:2] in ['xm', 'ym']: tab[c] = 2*tab_in[c] else: tab[c] = tab_in[c] sh = sci_data.shape mask = np.zeros(sh, dtype=bool) seg = np.zeros(sh, dtype=np.int) for i in [0,1]: for j in [0,1]: mask[j::2, i::2] = mask_in seg[j::2, i::2] = seg_in else: to_flam = im[0].header['PHOTFLAM'] to_fnu = im[0].header['PHOTFNU'] tab = tab_in mask = mask_in seg = seg_in if bg_mask_in is None: bg_mask = (~mask) & wht_mask & (seg <= 0) else: bg_mask = bg_mask_in if bkg_in is None: bkg = np.median(sci_data[bg_mask]) else: bkg = bkg_in sci_data -= bkg var_data[~wht_mask] = 0 bin_flux, bin_err, bin_area = prep.get_seg_iso_flux(sci_data, seg, tab, err=np.sqrt(var_data)) image_flux = prep.get_seg_iso_flux(sci_data, seg, tab, fill=bin_flux/bin_area) image_err = prep.get_seg_iso_flux(sci_data, seg, tab, fill=bin_err/bin_area) image_flux[mask] = sci_data[mask]*1 image_err[mask] = np.sqrt(var_data)[mask] res = {} res['bin_flux'] = bin_flux res['bin_err'] = bin_err res['bin_area'] = bin_area if IS_ACS: res['to_flam'] = to_flam res['to_fnu'] = to_fnu res['image_flux'] = image_flux[0::2, 0::2]*4 res['image_err'] = image_err[0::2, 0::2]*4 else: res['to_flam'] = to_flam res['to_fnu'] = to_fnu res['image_flux'] = image_flux res['image_err'] = image_err res['bkg'] = bkg res['bg_mask'] = bg_mask data_tab = {} data_tab['sci'] = sci_data data_tab['err'] = np.sqrt(var_data) data_tab['mask'] = mask return res, data_tab