def make_mf(maskname, instrument, filtname, npix, peakmethod='fft', n_wl=3, theta_detector=0, cutoff=1e-4, hole_diam=0.8, fw_splodge=0.7, verbose=False, diag_plot=False, display=True): """ Summary: -------- Compute the match filter mf which give the indices of the peak positions (mf.pvct) and the associated gains (mf.gvct) in the image. Contains also the u-v coordinates, wavelengths informations, holes mask positions (mf.xy_coords), centered mf (mf.cpvct, mf.gpvct), etc. Parameters: ----------- `maskname`: str Name of the mask (number of holes),\n `instrument`: str Instrument used (default = jwst),\n `filtname`: str Name of the filter,\n `npix`: int Size of the image,\n `peakmethod` {str}: 3 methods are used to sample the u-v space: 'fft' uses fft between individual holes to compute the expected splodge positions; 'square' compute the splodge in a square using the expected fraction of pixel to determine its weight; 'gauss' considers a gaussian splodge (with a gaussian weight) to get the same splodge side for each n(n-1)/2 baselines,\n `n_wl`: int number of wavelengths to use to simulate bandwidth,\n `theta_detector`: float Angle [deg] to rotate the mask compare to the detector (if the mask is not perfectly aligned with the detector, e.g.: VLT/VISIR) ,\n `cutoff`: float cutoff limit between noise and signal pixels in simulated transforms,\n `hole_diam`: float Diameter of a single aperture (0.8 for JWST),\n `fw_splodge` {float}: Relative size of the splodge used to compute multiple triangle indices and the fwhm of the 'gauss' technique,\n """ # Get detector, filter and mask informations # ------------------------------------------ pixelsize = get_pixel_size(instrument) # Pixel size of the detector [rad] # Wavelength of the filter (filt[0]: central, filt[1]: width) filt = get_wavelength(instrument, filtname) xy_coords = get_mask(instrument, maskname) # mask coordinates x_mask = xy_coords[:, 0] y_mask = xy_coords[:, 1] x_mask_rot = x_mask*np.cos(np.deg2rad(theta_detector)) + \ y_mask*np.sin(np.deg2rad(theta_detector)) y_mask_rot = -x_mask*np.sin(np.deg2rad(theta_detector)) + \ y_mask*np.cos(np.deg2rad(theta_detector)) xy_coords_rot = [] for i in range(len(x_mask)): xy_coords_rot.append([x_mask_rot[i], y_mask_rot[i]]) xy_coords = np.array(xy_coords_rot) if display: _plot_mask_coord(xy_coords, maskname, instrument) n_holes = xy_coords.shape[0] index_mask = compute_index_mask(n_holes) n_baselines = index_mask.n_baselines n_bispect = index_mask.n_bispect ncp_i = int((n_holes - 1)*(n_holes - 2)/2) if verbose: cprint('---------------------------', 'cyan') cprint('%s (%s): %i holes masks' % (instrument.upper(), filtname, n_holes), 'cyan') cprint('---------------------------', 'cyan') cprint('nbl = %i, nbs = %i, ncp_i = %i, ncov = %i' % (n_baselines, n_bispect, ncp_i, index_mask.n_cov), 'cyan') # Consider the filter to be made up of n_wl wavelengths wl = np.arange(n_wl)/n_wl*filt[1] wl = wl - np.mean(wl) + filt[0] Sum, Sum_c = 0, 0 mf_ix = np.zeros([2, n_baselines], dtype=int) # matched filter mf_ix_c = np.zeros([2, n_baselines], dtype=int) # matched filter if verbose: print('\n- Calculating sampling of', n_holes, 'holes array...') innerpix, innerpix_center = _compute_center_splodge(npix, pixelsize, filt, hole_diam=hole_diam) u, v = _compute_uv_coord(xy_coords, index_mask, filt, pixelsize, npix, round_uv_to_pixel=False) mf_pvct = mf_gvct = mfc_pvct = mfc_gvct = None for i in range(n_baselines): args = {'i': i, 'npix': npix, 'pixelsize': pixelsize, 'innerpix': innerpix, 'innerpix_center': innerpix_center} if peakmethod == 'fft': ind_peak = _peak_fft_method(xy_coords=xy_coords, wl=wl, index_mask=index_mask, **args) elif peakmethod == 'square': ind_peak = _peak_square_method(u=u, v=v, **args) elif peakmethod == 'one': ind_peak = _peak_one_method(u=u, v=v, **args) elif peakmethod == 'gauss': ind_peak = _peak_gauss_method(u=u, v=v, filt=filt, index_mask=index_mask, fw_splodge=fw_splodge, **args, hole_diam=hole_diam) else: cprint( "Error: choose the extraction method 'gauss', 'fft' or 'square'.", 'red') return None # Compute the cutoff limit before saving the gain map pixelvector = np.where(ind_peak['flat'] >= cutoff)[0] pixelvector_c = np.where(ind_peak['centered'] >= cutoff)[0] # Now normalise the pixel gain, so that using the matched filter # on an ideal splodge is equivalent to just looking at the peak... if peakmethod == 'gauss': pixelgain, pixelgain_c = _normalize_gain(ind_peak['gain_f'], ind_peak['gain_c'], pixelvector, pixelvector_c) else: pixelgain, pixelgain_c = _normalize_gain(ind_peak['flat'], ind_peak['centered'], pixelvector, pixelvector_c) mf_ix[0, i] = Sum Sum = Sum + len(pixelvector) mf_ix[1, i] = Sum mf_ix_c[0, i] = Sum_c Sum_c = Sum_c + len(pixelvector_c) mf_ix_c[1, i] = Sum_c if (i == 0): mf_pvct = list(pixelvector) mf_gvct = list(pixelgain) mfc_pvct = list(pixelvector_c) mfc_gvct = list(pixelgain_c) else: mf_pvct.extend(list(pixelvector)) mf_gvct.extend(list(pixelgain)) mfc_pvct.extend(list(pixelvector_c)) mfc_gvct.extend(list(pixelgain_c)) mf = np.zeros([npix, npix, n_baselines], dtype=[('norm', float), ('conj', float), ('norm_c', float), ('conj_c', float)]) for i in range(n_baselines): mf_tmp = np.zeros([npix, npix]) mf_tmp_c = np.zeros([npix, npix]) ind = mf_pvct[mf_ix[0, i]:mf_ix[1, i]] ind_c = mfc_pvct[mf_ix_c[0, i]:mf_ix_c[1, i]] mf_tmp.ravel()[ind] = mf_gvct[mf_ix[0, i]:mf_ix[1, i]] mf_tmp_c.ravel()[ind_c] = mfc_gvct[mf_ix_c[0, i]:mf_ix_c[1, i]] mf_tmp = mf_tmp.reshape([npix, npix]) mf_tmp_c = mf_tmp_c.reshape([npix, npix]) mf['norm'][:, :, i] = np.roll(mf_tmp, 0, axis=1) mf['norm_c'][:, :, i] = np.roll(mf_tmp_c, 0, axis=1) mf_temp_rot = np.roll( np.roll(np.rot90(np.rot90(mf_tmp)), 1, axis=0), 1, axis=1) mf_temp_rot_c = np.roll( np.roll(np.rot90(np.rot90(mf_tmp_c)), 1, axis=0), 1, axis=1) mf['conj'][:, :, i] = mf_temp_rot mf['conj_c'][:, :, i] = mf_temp_rot_c norm = np.sqrt(np.sum(mf['norm'][:, :, i]**2)) mf['norm'][:, :, i] = mf['norm'][:, :, i]/norm mf['conj'][:, :, i] = mf['conj'][:, :, i]/norm mf['norm_c'][:, :, i] = mf['norm_c'][:, :, i]/norm mf['conj_c'][:, :, i] = mf['conj_c'][:, :, i]/norm rmat, imat = _make_overlap_mat(mf, n_baselines, display=diag_plot) mf_tot = np.sum(mf['norm'], axis=2) + np.sum(mf['conj'], axis=2) mf_tot_m = np.sum(mf['norm'], axis=2) - np.sum(mf['conj'], axis=2) im_uv = np.roll(np.fft.fftshift(mf_tot), 1, axis=1) if display: plt.figure(figsize=(6, 6)) plt.title('(u-v) plan - mask %s' % (maskname), fontsize=14) plt.imshow(im_uv, origin='lower') plt.plot(npix//2+1, npix//2, 'r+') plt.ylabel('Y [pix]') # , fontsize=12) plt.xlabel('X [pix]') # , fontsize=12) plt.tight_layout() out = {'cube': mf['norm'], 'imat': imat, 'rmat': rmat, 'uv': im_uv, 'tot': mf_tot, 'tot_m': mf_tot_m, 'pvct': mf_pvct, 'gvct': mf_gvct, 'cpvct': mfc_pvct, 'cgvct': mfc_gvct, 'ix': mf_ix, 'u': u*filt[0], 'v': v*filt[0], 'wl': filt[0], 'e_wl': filt[1], 'pixelSize': pixelsize, 'xy_coords': xy_coords } return dict2class(out)
def make_mf( maskname, instrument, filtname, npix, i_wl=None, peakmethod="fft", n_wl=3, theta_detector=0, cutoff=1e-4, hole_diam=0.8, fw_splodge=0.7, scaling=1, diag_plot=False, verbose=False, display=True, save_to=None, filename=None, ): """ Summary: -------- Compute the match filter mf which give the indices of the peak positions (mf.pvct) and the associated gains (mf.gvct) in the image. Contains also the u-v coordinates, wavelengths informations, holes mask positions (mf.xy_coords), centered mf (mf.cpvct, mf.gpvct), etc. Parameters: ----------- `maskname`: str Name of the mask (number of holes),\n `instrument`: str Instrument used (default = jwst),\n `filtname`: str Name of the filter,\n `npix`: int Size of the image,\n `peakmethod` {str}: 3 methods are used to sample the u-v space: 'fft' uses fft between individual holes to compute the expected splodge positions; 'square' compute the splodge in a square using the expected fraction of pixel to determine its weight; 'gauss' considers a gaussian splodge (with a gaussian weight) to get the same splodge side for each n(n-1)/2 baselines,\n `n_wl`: int number of wavelengths to use to simulate bandwidth,\n `theta_detector`: float Angle [deg] to rotate the mask compare to the detector (if the mask is not perfectly aligned with the detector, e.g.: VLT/VISIR) ,\n `cutoff`: float cutoff limit between noise and signal pixels in simulated transforms,\n `hole_diam`: float Diameter of a single aperture (0.8 for JWST),\n `fw_splodge` {float}: Relative size of the splodge used to compute multiple triangle indices and the fwhm of the 'gauss' technique,\n """ from munch import munchify as dict2class # Get detector, filter and mask informations # ------------------------------------------ pixelsize = get_pixel_size(instrument) # Pixel size of the detector [rad] if pixelsize is np.nan: cprint("Error: Pixel size unknown for %s." % instrument, "red") return None # Wavelength of the filter (filt[0]: central, filt[1]: width) filt = get_wavelength(instrument, filtname) if instrument == "SPHERE-IFS": if isinstance(i_wl, (int, np.integer)): filt = [filt[i_wl], 0.001 * filt[i_wl]] else: filt = [np.mean(filt[i_wl[0] : i_wl[1]]), filt[i_wl[1]] - filt[i_wl[0]]] xy_coords = get_mask(instrument, maskname) # mask coordinates x_mask = xy_coords[:, 0] * scaling y_mask = xy_coords[:, 1] * scaling x_mask_rot = x_mask * np.cos(np.deg2rad(theta_detector)) + y_mask * np.sin( np.deg2rad(theta_detector) ) y_mask_rot = -x_mask * np.sin(np.deg2rad(theta_detector)) + y_mask * np.cos( np.deg2rad(theta_detector) ) xy_coords_rot = [] for i in range(len(x_mask)): xy_coords_rot.append([x_mask_rot[i], y_mask_rot[i]]) xy_coords = np.array(xy_coords_rot) if display: import matplotlib.pyplot as plt _plot_mask_coord(xy_coords, maskname, instrument) if save_to is not None: figname = os.path.join(save_to, Path(filename).stem) plt.savefig(f"{figname}_{1}.pdf") n_holes = xy_coords.shape[0] index_mask = compute_index_mask(n_holes) n_baselines = index_mask.n_baselines n_bispect = index_mask.n_bispect ncp_i = int((n_holes - 1) * (n_holes - 2) / 2) if verbose: cprint("---------------------------", "cyan") cprint( "%s (%s): %i holes masks" % (instrument.upper(), filtname, n_holes), "cyan" ) cprint("---------------------------", "cyan") cprint( "nbl = %i, nbs = %i, ncp_i = %i, ncov = %i" % (n_baselines, n_bispect, ncp_i, index_mask.n_cov), "cyan", ) # Consider the filter to be made up of n_wl wavelengths wl = np.arange(n_wl) / n_wl * filt[1] wl = wl - np.mean(wl) + filt[0] Sum, Sum_c = 0, 0 mf_ix = np.zeros([2, n_baselines], dtype=int) # matched filter mf_ix_c = np.zeros([2, n_baselines], dtype=int) # matched filter if verbose: print("\n- Calculating sampling of", n_holes, "holes array...") innerpix, innerpix_center = _compute_center_splodge( npix, pixelsize, filt, hole_diam=hole_diam ) u, v = _compute_uv_coord( xy_coords, index_mask, filt, pixelsize, npix, round_uv_to_pixel=False ) mf_pvct = mf_gvct = mfc_pvct = mfc_gvct = None for i in range(n_baselines): args = { "i": i, "npix": npix, "pixelsize": pixelsize, "innerpix": innerpix, "innerpix_center": innerpix_center, } if peakmethod == "fft": ind_peak = _peak_fft_method( xy_coords=xy_coords, wl=wl, index_mask=index_mask, **args ) elif peakmethod == "square": ind_peak = _peak_square_method(u=u, v=v, **args) elif peakmethod == "unique": ind_peak = _peak_one_method(u=u, v=v, **args) elif peakmethod == "gauss": ind_peak = _peak_gauss_method( u=u, v=v, filt=filt, index_mask=index_mask, fw_splodge=fw_splodge, **args, hole_diam=hole_diam, ) else: cprint( "Error: choose the extraction method 'gauss', 'fft' or 'square'.", "red" ) return None # Compute the cutoff limit before saving the gain map pixelvector = np.where(ind_peak["flat"] >= cutoff)[0] pixelvector_c = np.where(ind_peak["centered"] >= cutoff)[0] # Now normalise the pixel gain, so that using the matched filter # on an ideal splodge is equivalent to just looking at the peak... if peakmethod == "gauss": pixelgain, pixelgain_c = _normalize_gain( ind_peak["gain_f"], ind_peak["gain_c"], pixelvector, pixelvector_c ) else: pixelgain, pixelgain_c = _normalize_gain( ind_peak["flat"], ind_peak["centered"], pixelvector, pixelvector_c ) mf_ix[0, i] = Sum Sum = Sum + len(pixelvector) mf_ix[1, i] = Sum mf_ix_c[0, i] = Sum_c Sum_c = Sum_c + len(pixelvector_c) mf_ix_c[1, i] = Sum_c if i == 0: mf_pvct = list(pixelvector) mf_gvct = list(pixelgain) mfc_pvct = list(pixelvector_c) mfc_gvct = list(pixelgain_c) else: mf_pvct.extend(list(pixelvector)) mf_gvct.extend(list(pixelgain)) mfc_pvct.extend(list(pixelvector_c)) mfc_gvct.extend(list(pixelgain_c)) mf = np.zeros( [npix, npix, n_baselines], dtype=[("norm", float), ("conj", float), ("norm_c", float), ("conj_c", float)], ) for i in range(n_baselines): mf_tmp = np.zeros([npix, npix]) mf_tmp_c = np.zeros([npix, npix]) ind = mf_pvct[mf_ix[0, i] : mf_ix[1, i]] ind_c = mfc_pvct[mf_ix_c[0, i] : mf_ix_c[1, i]] mf_tmp.ravel()[ind] = mf_gvct[mf_ix[0, i] : mf_ix[1, i]] mf_tmp_c.ravel()[ind_c] = mfc_gvct[mf_ix_c[0, i] : mf_ix_c[1, i]] mf_tmp = mf_tmp.reshape([npix, npix]) mf_tmp_c = mf_tmp_c.reshape([npix, npix]) mf["norm"][:, :, i] = np.roll(mf_tmp, 0, axis=1) mf["norm_c"][:, :, i] = np.roll(mf_tmp_c, 0, axis=1) mf_temp_rot = np.roll(np.roll(np.rot90(np.rot90(mf_tmp)), 1, axis=0), 1, axis=1) mf_temp_rot_c = np.roll( np.roll(np.rot90(np.rot90(mf_tmp_c)), 1, axis=0), 1, axis=1 ) mf["conj"][:, :, i] = mf_temp_rot mf["conj_c"][:, :, i] = mf_temp_rot_c norm = np.sqrt(np.sum(mf["norm"][:, :, i] ** 2)) mf["norm"][:, :, i] = mf["norm"][:, :, i] / norm mf["conj"][:, :, i] = mf["conj"][:, :, i] / norm mf["norm_c"][:, :, i] = mf["norm_c"][:, :, i] / norm mf["conj_c"][:, :, i] = mf["conj_c"][:, :, i] / norm rmat, imat = _make_overlap_mat(mf, n_baselines, display=diag_plot) mf_tot = np.sum(mf["norm"], axis=2) + np.sum(mf["conj"], axis=2) mf_tot_m = np.sum(mf["norm"], axis=2) - np.sum(mf["conj"], axis=2) im_uv = np.roll(np.fft.fftshift(mf_tot), 1, axis=1) if display: import matplotlib.pyplot as plt plt.figure(figsize=(9, 7)) plt.title("(u-v) plan - mask %s" % (maskname), fontsize=14) plt.imshow(im_uv, origin="lower") plt.plot(npix // 2 + 1, npix // 2, "r+") plt.ylabel("Y [pix]") # , fontsize=12) plt.xlabel("X [pix]") # , fontsize=12) plt.tight_layout() out = { "cube": mf["norm"], "imat": imat, "rmat": rmat, "uv": im_uv, "tot": mf_tot, "tot_m": mf_tot_m, "pvct": mf_pvct, "gvct": mf_gvct, "cpvct": mfc_pvct, "cgvct": mfc_gvct, "ix": mf_ix, "u": u * filt[0], "v": v * filt[0], "wl": filt[0], "e_wl": filt[1], "pixelSize": pixelsize, "xy_coords": xy_coords, } return dict2class(out)
def test_getPixel(ins): p = get_pixel_size(ins) assert isinstance(p, float)