def scarlet1_initialize(images, peaks, psfs, variances, bands): """ Deblend input images with scarlet Args: images: Numpy array of multi-band image to run scarlet on [Number of bands, height, width]. peaks: Array of x and y coordinates of centroids of objects in the image [number of sources, 2]. bg_rms: Background RMS value of the images [Number of bands] iters: Maximum number of iterations if scarlet doesn't converge (Default: 200). e_rel: Relative error for convergence (Default: 0.015) Returns blend: scarlet.Blend object for the initialized sources rejected_sources: list of sources (if any) that scarlet was unable to initialize the image with. """ model_psf = scarlet.PSF(partial(scarlet.psf.gaussian, sigma=0.8), shape=(None, 41, 41)) model_frame = scarlet.Frame(images.shape, psfs=model_psf, channels=bands) observation = scarlet.Observation(images, psfs=scarlet.PSF(psfs), weights=1./variances, channels=bands).match(model_frame) sources = [] for n, peak in enumerate(peaks): result = scarlet.ExtendedSource(model_frame, (peak[1], peak[0]), observation, symmetric=True, monotonic=True, thresh=1, shifting=True) sed = result.sed morph = result.morph if np.all([s < 0 for s in sed]) or np.sum(morph) == 0: raise ValueError("Incorrectly initialized") sources.append(result) blend = scarlet.Blend(sources, observation) return blend, observation
def _get_scar_model(self): im, psf_im, coords, dims, dx1, dy1, noise = Simulation.__call__(self) bg_rms = self['Image']['Bgrms'] mode = self['Mode'] constraints = {"S": None, "m": {'use_nearest': False}, "+": None} #constraints['l0'] = bg_rms sources = [ scarlet.ExtendedSource(coord, im, [bg_rms]) for coord in coords ] #scarlet.ExtendedSource.shift_center=0.0 #config = scarlet.Config(edge_flux_thresh=0.05) blend = scarlet.Blend(sources, im, bg_rms=[bg_rms]) #,config=config) blend.fit(10000, e_rel=1e-3) model = blend.get_model() #plt.imshow(im[0,:,:]) #plt.colorbar() #plt.savefig('test.png') mod1 = blend.get_model(m=0) mod2 = blend.get_model(m=1) cen_mod = sources[0].get_model() #output['mod_size_flag'][j] = 0 #if np.shape(cen_mod) != (1,25,25): #output['mod_size_flag'][j] = 3 neigh_mod = sources[1].get_model() #steps_used = blend.it return im, psf_im, model, mod1, mod2, cen_mod, neigh_mod, coords, dx1, dy1, noise
def scarlet_initialize(images, peaks, bg_rms): """ Deblend input images with scarlet Args: images: Numpy array of multi-band image to run scarlet on [Number of bands, height, width]. peaks: Array of x and y coordinates of centroids of objects in the image [number of sources, 2]. bg_rms: Background RMS value of the images [Number of bands] iters: Maximum number of iterations if scarlet doesn't converge (Default: 200). e_rel: Relative error for convergence (Default: 0.015) Returns blend: scarlet.Blend object for the initialized sources rejected_sources: list of sources (if any) that scarlet was unable to initialize the image with. """ sources = [] for n, peak in enumerate(peaks): try: result = scarlet.ExtendedSource( (peak[1], peak[0]), images, bg_rms) sources.append(result) except scarlet.source.SourceInitError: print("No flux in peak {0} at {1}".format(n, peak)) blend = scarlet.Blend(sources).set_data(images, bg_rms=bg_rms) return blend
def _get_model(self): im,psf_im,coords,dims,sing_im,sing_coord = Simulation.__call__(self) bg_rms = self['Image']['Bgrms'] mode = self['Mode'] if mode == 'scarlet': constraints = {"S": None, "m": {'use_nearest': False}, "+": None} #constraints['l1'] = bg_rms sources = [scarlet.ExtendedSource(coord, im, [bg_rms]) for coord in coords] #config = scarlet.Config(edge_flux_thresh=0.05) #scarlet.ExtendedSource.shift_center=0.0 blend = scarlet.Blend(sources, im, bg_rms=[bg_rms])#,config=config) blend.fit(10000, e_rel=1e-3) model = blend.get_model() mod1 = blend.get_model(m=0) #mod2 = blend.get_model(m=1) cen_mod = sources[0].get_model() #mod_size_flag = 0 #if np.shape(cen_mod) != (1,25,25): # mod_size_flag = 3 #print("not 25x25") #output['mod_size_flag'][j] = 3 #neigh_mod = sources[1].get_model() #steps_used = blend.it #return im,psf_im,model,mod1,mod2,cen_mod,neigh_mod,coords,sing_im,sing_coord return im,psf_im,model,mod1,cen_mod,coords,sing_im,sing_coord
def test_fit_point_source(self): shape = (6, 31, 55) coords = [(20, 10), (10, 30), (17, 42)] amplitudes = [3, 2, 1] result = init_data(shape, coords, amplitudes, dtype=np.float64) target_psf, psfs, images, channels, seds, morphs = result B, Ny, Nx = shape frame = scarlet.Frame(images.shape, psfs=target_psf[None], dtype=np.float64) observation = scarlet.Observation(images, psfs=psfs).match(frame) sources = [ scarlet.PointSource(frame, coord, observation) for coord in coords ] blend = scarlet.Blend(sources, observation) # Try to run for 10 iterations # Since the model is already near exact, it should converge # on the 2nd iteration (since it doesn't calculate the initial loss) blend.fit(10) assert blend.it == 2 assert_almost_equal(blend.mse, [3.875628098330452e-15, 3.875598349723412e-15], decimal=10) assert blend.mse[0] > blend.mse[1]
def test_fit_extended_source(self): shape = (6, 31, 55) coords = [(20, 10), (10, 30), (17, 42)] amplitudes = [3, 2, 1] result = init_data(shape, coords, amplitudes, dtype=np.float64) target_psf, psfs, images, channels, seds, morphs = result B, Ny, Nx = shape frame = scarlet.Frame(images.shape, psfs=target_psf[None], dtype=np.float64) observation = scarlet.Observation(images, psfs=psfs).match(frame) bg_rms = np.ones((B, )) sources = [ scarlet.ExtendedSource(frame, coord, observation, bg_rms) for coord in coords ] blend = scarlet.Blend(sources, observation) # Scale the input psfs by the observation and model psfs to ensure # the sources were initialized correctly psf_scale = observation.frame.psfs.max(axis=(1, 2)) / frame.psfs[0].max() scaled_seds = np.array([c.sed * psf_scale for c in blend.components]) assert_almost_equal(scaled_seds, seds) # Fit the model blend.fit(100) assert blend.it < 20 mse = np.array(blend.mse[:-1]) _mse = np.array(blend.mse[1:]) assert np.all(mse - _mse >= 0)
def get_scar_model(self): import scarlet import scarlet.constraint as sc im, psf_im, coords, dims, dx1, dy1, noise = self.sim() bg_rms = self.sim['Image']['Bgrms'] bg_rms = bg_rms / np.sqrt(len(im)) mode = self.sim['Mode'] #create multiband background im bg = np.zeros((len(im))) bg += bg_rms #choose constraints for PSF and sources (optional) #psf_constraints = ()#sc.SimpleConstraint()) #source_constraints = ()#sc.SimpleConstraint(),sc.DirectSymmetryConstraint()) #sc.DirectMonotonicityConstraint(use_nearest=False) config = scarlet.Config(source_sizes=[25]) #create multiband PSF image #psf_dims = np.shape(psf_im) #psf_im3d = psf_im.reshape( (1, psf_dims[0], psf_dims[1]) ) #psfs = np.zeros((len(im), psf_dims[0],psf_dims[1])) #psfs += psf_im3d #PSF Matching #target_psf = scarlet.psf_match.fit_target_psf(psfs, # scarlet.psf_match.gaussian) #diff_kernels, psf_blend = scarlet.psf_match.build_diff_kernels(psfs, # target_psf) #if use PSF matching, set psf = diff_kernels below sources = [ scarlet.ExtendedSource(coord, im, bg_rms=bg, psf=None, config=config) for coord in coords ] #config = scarlet.Config(edge_flux_thresh=0.05) blend = scarlet.Blend(sources) blend.set_data(im, bg_rms=bg, config=config) blend.fit(10000, e_rel=1e-3) #get full sized scarlet models model = blend.get_model() mod1 = blend.get_model(0) mod2 = blend.get_model(1) #get postage stamp models cen_mod = sources[0].get_model() neigh_mod = sources[1].get_model() #get scarlet coordinates cen_cen_pos = blend[0][0].center neigh_cen_pos = blend[1][0].center return im, psf_im, model, mod1, mod2, cen_mod, neigh_mod, coords, dx1, dy1, noise, cen_cen_pos, neigh_cen_pos
def blend(sources, observation): blend = scarlet.Blend(sources, observation) blend.fit(200,1e-7) print("scarlet ran for {0} iterations to logL = {1}".format(len(blend.loss), -blend.loss[-1])) plt.plot(-np.array(blend.loss)) plt.xlabel('Iteration') plt.ylabel('log-Likelihood') plt.savefig("loglikehood"+basename+".pdf") plt.close() return
def _get_model(self): im, psf_im, coords, dims, sing_im, sing_coord = Simulation.__call__( self) bg_rms = self['Image']['Bgrms'] mode = self['Mode'] #constraints = {"S": None, "m": {'use_nearest': False}, "+": None} constraint = (sc.SimpleConstraint()) #constraint = PSFConstraint() config = (scarlet.Config(source_sizes=[25])) #psf_dims = np.shape(psf_im) #psf_im3d = psf_im.reshape( (1, psf_dims[0], psf_dims[1]) ) #target_psf = scarlet.psf_match.fit_target_psf(psf_im3d, scarlet.psf_match.gaussian) #diff_kernels, psf_blend = scarlet.psf_match.build_diff_kernels(psf_im3d, target_psf,constraints=constraint) #constraint = (sc.L0Constraint(.5*bg_rms)) #& sc.DirectSymmetryConstraint(sigma=0.25)) #& sc.MonotonicityConstraint(use_nearest=True)) #constraints['l1'] = bg_rms sources = [ scarlet.ExtendedSource(coord, im, [bg_rms], psf=None, config=config, shift_center=0.0) for coord in coords ] #config = scarlet.Config(edge_flux_thresh=1.25) #scarlet.ExtendedSource.shift_center=0.0 blend = scarlet.Blend(sources) blend.set_data(im, bg_rms=[bg_rms], config=config) blend.fit(10000, e_rel=1e-3) model = blend.get_model() mod1 = blend.get_model(0) #mod2 = blend.get_model(1) cen_mod = sources[0].get_model() #cen_pos = sources[0].center #neigh_mod = sources[1].get_model() #print(cen_pos[0]-coords[0][0],cen_pos[1]-coords[0][1]) #psf_model = psf_blend.get_model() """ masked_mod = np.ma.masked_equal(diff_kernels,0.0) f,ax = plt.subplots(1,2,figsize=(8,4)) f1 = ax[0].imshow(psf_im3d[0,:,:]) ax[0].set_title("Orig PSF") plt.colorbar(f1,ax=ax[0]) f2 = ax[1].imshow(masked_mod[0,:,:]) plt.colorbar(f2,ax=ax[1]) ax[1].set_title("Diff Kernels") plt.tight_layout() f.savefig('test.png') plt.close() steps_used = blend.it print(steps_used) """ #return im,psf_im,model,mod1,mod2,cen_mod,neigh_mod,coords,sing_im,sing_coord return im, psf_im, model, mod1, cen_mod, coords, sing_im, sing_coord
def initialize(images, peaks, diff_kernels, bg_rms): sources = [] rejected_sources = [] for n, peak in enumerate(peaks): try: result = scarlet.ExtendedSource((peak[1], peak[0]), images, bg_rms, psf=diff_kernels) sources.append(result) except scarlet.source.SourceInitError: rejected_sources.append(n) print("No flux in peak {0} at {1}".format(n, peak)) blend = scarlet.Blend(sources, images, bg_rms=bg_rms) return blend, rejected_sources
def mu_central(components, observation=None, method='centroid', zeropoint=27.0, pixel_scale=0.168, weight_order=0): """ Determine the central surface brightness, by calculating the average of 9 pixels around the centroid Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Component to analyze observation method: 'centroid' or 'winpos' """ if not isinstance(components, list): components = [components] if method == 'winpos': y_cen, x_cen = winpos(components, observation=observation) y_cen = y_cen.mean() x_cen = x_cen.mean() else: # Determine the centroid, averaged through channels _, y_cen, x_cen = centroid(components, observation=observation) blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask depth = model.shape[0] mu_cen = [] if depth > 1: for i in range(depth): img = model[i] mu = img[int(y_cen) - 1:int(y_cen) + 2, int(x_cen) - 1:int(x_cen) + 2].mean() mu_cen.append(mu) mu_cen = -2.5 * np.log10(np.array(mu_cen) / (pixel_scale**2)) + zeropoint return mu_cen
def flux(components, observation): """Determine flux in every channel Parameters ---------- component: `scarlet.Component` or `scarlet.ComponentTree` Component to analyze """ tot_flux = 0 if not isinstance(components, list): components = [components] mask = (observation.weights == 0) blend = scarlet.Blend(components, observation) model = blend.get_model() tot_flux = (model * ~mask).sum(axis=(1, 2)) return tot_flux
def get_scar_model(self): import scarlet import scarlet.constraint as sc im, psf_im, coords, dims, dx1, dy1, noise = self.sim() bg_rms = self.sim['Image']['Bgrms'] mode = self.sim['Mode'] bg = np.zeros((len(im))) bg += bg_rms #psf_constraints = (sc.SimpleConstraint()) #source_constraints = ()#sc.SimpleConstraint(),sc.DirectSymmetryConstraint()) #sc.DirectMonotonicityConstraint(use_nearest=False) config = scarlet.Config(source_sizes=[25]) psf_dims = np.shape(psf_im) psf_im3d = psf_im.reshape((1, psf_dims[0], psf_dims[1])) psfs = np.zeros((len(im), psf_dims[0], psf_dims[1])) psfs += psf_im3d #target_psf = scarlet.psf_match.fit_target_psf(psfs, # scarlet.psf_match.gaussian) #diff_kernels, psf_blend = scarlet.psf_match.build_diff_kernels(psfs, # target_psf, constraints = None) sources = [ scarlet.ExtendedSource(coord, im, bg_rms=bg, psf=None, config=config) for coord in coords ] #config = scarlet.Config(edge_flux_thresh=0.05) blend = scarlet.Blend(sources) blend.set_data(im, bg_rms=bg, config=config) blend.fit(10000, e_rel=1e-3) model = blend.get_model() mod1 = blend.get_model(0) #mod2 = blend.get_model(1) cen_mod = sources[0].get_model() #neigh_mod = sources[1].get_model() #psf_model = psf_blend.get_model() #steps_used = blend.it cen_cen_pos = blend[0][0].center #return im,psf_im,model,mod1,mod2,cen_mod,neigh_mod,coords,dx1,dy1,noise return im, psf_im, model, mod1, cen_mod, coords, dx1, dy1, noise, cen_cen_pos, psfs
def test_model_render(self): shape = (6, 31, 55) coords = [(20, 10), (10, 30), (17, 42)] result = init_data(shape, coords, [3, 2, 1], dtype=np.float64) target_psf, psfs, images, channels, seds, morphs = result # Test init with psfs frame = scarlet.Frame(images.shape, psfs=target_psf[None], dtype=np.float64) observation = scarlet.Observation(images, psfs=psfs).match(frame) sources = [ scarlet.PointSource(frame, coord, observation) for coord in coords ] blend = scarlet.Blend(sources, observation) model = observation.render(blend.get_model()) assert_almost_equal(images, model, decimal=5) for s0, s in zip(sources, blend.sources): assert_array_equal(s.get_model(), s0.get_model())
def centroid(components, observation=None): """Determine centroid of (multiple) components Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Components to analyze Returns ------- y, x """ if not isinstance(components, list): components = [components] blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask indices = np.indices(model.shape) centroid = np.array([np.sum(ind * model) for ind in indices]) / model.sum() return centroid
def kron_radius(components, observation=None, weight_order=0): """ Determine the Kron Radius Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Component to analyze observation """ if not isinstance(components, list): components = [components] # Determine the centroid, averaged through channels _, y_cen, x_cen = centroid(components, observation=observation) s = shape(components, observation, show_fig=False, weight_order=weight_order) q = s['q'] theta = np.deg2rad(s['pa']) blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask depth = model.shape[0] kron = [] if depth > 1: for i in range(depth): r_max = max(model.shape) r = sep.kron_radius(model[i], x_cen, y_cen, 1, 1 * q[i], theta[i], r_max)[0] kron.append(r) return np.array(kron)
def winpos(components, observation=None): """Calculate more accurate object centroids using ‘windowed’ algorithm. https://sep.readthedocs.io/en/v1.0.x/api/sep.winpos.html Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Components to analyze Returns ------- y, x: winpos in each channel """ if not isinstance(components, list): components = [components] # Determine the centroid, averaged through channels _, y_cen, x_cen = centroid(components, observation=observation) blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask R50 = flux_radius(components, observation, frac=0.5) sig = 2. / 2.35 * R50 # R50 is half-light radius for each channel depth = model.shape[0] x_ = [] y_ = [] if depth > 1: for i in range(depth): xwin, ywin, flag = sep.winpos(model[i], x_cen, y_cen, sig[i]) x_.append(xwin) y_.append(ywin) return np.array(y_), np.array(x_)
def get_scar_model(self): import scarlet im, psf_im, coords, dims, dx1, dy1, noise, sep_coords = self.sim() bg_rms = self.sim['Image']['Bgrms'] mode = self.sim['Mode'] #constraints = {"S": None, "m": {'use_nearest': False}, "+": None} #constraints['l0'] = bg_rms constraint = (sc.SimpleConstraint()) # & sc.MonotonicityConstraint(use_nearest=True)) config = scarlet.Config(source_sizes=[25]) psf_dims = np.shape(psf_im) psf_im3d = psf_im.reshape((1, psf_dims[0], psf_dims[1])) target_psf = scarlet.psf_match.fit_target_psf( psf_im3d, scarlet.psf_match.gaussian) diff_kernels, psf_blend = scarlet.psf_match.build_diff_kernels( psf_im3d, target_psf, constraints=constraint.copy()) sources = [ scarlet.ExtendedSource(coord, im, [bg_rms], psf=diff_kernels, config=config) for coord in coords ] #sources = [scarlet.ExtendedSource(coord, im, [bg_rms]) for coord in coords] #scarlet.ExtendedSource.shift_center=0.0 #config = scarlet.Config(edge_flux_thresh=0.05) blend = scarlet.Blend(sources, im, bg_rms=[bg_rms], config=config) blend.fit(10000, e_rel=1e-3) model = blend.get_model() mod1 = blend.get_model(m=0) mod2 = blend.get_model(m=1) cen_mod = sources[0].get_model() neigh_mod = sources[1].get_model() #steps_used = blend.it return im, psf_im, model, mod1, mod2, cen_mod, neigh_mod, coords, dx1, dy1, noise, sep_coords
def flux_radius(components, observation=None, frac=0.5, weight_order=0): """ Determine the radius R (in pixels, along semi-major axis), the flux within R has a fraction of `frac` over the total flux. Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Component to analyze observation: frac: float fraction of lights within this R. """ from scipy.interpolate import interp1d, UnivariateSpline if not isinstance(components, list): components = [components] # Determine the centroid, averaged through channels _, y_cen, x_cen = centroid(components, observation=observation) s = shape(components, observation, show_fig=False, weight_order=weight_order) q = s['q'] theta = np.deg2rad(s['pa']) blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask total_flux = model.sum(axis=(1, 2)) depth = model.shape[0] r_frac = [] ## sep.sum_ellipse is very slow! Try to improve! if depth > 1: for i in range(depth): r_max = max(model.shape) r_ = np.linspace(0, r_max, 500) flux_ = sep.sum_ellipse(model[i], x_cen, y_cen, 1, 1 * q[i], theta[i], r=r_)[0] flux_ /= total_flux[i] func = UnivariateSpline(r_, flux_ - frac, s=0) r_frac.append(func.roots()[0]) else: # might be buggy r_max = max(model.shape) r_ = np.linspace(0, r_max, 500) flux_ = sep.sum_ellipse(model[0], x_cen, y_cen, 1, 1 * q[0], theta[0], r=r_)[0] flux_ /= total_flux[0] func = UnivariateSpline(r_, flux_ - frac, s=0) r_frac.append(func.roots()[0]) return np.array(r_frac)
def shape(components, observation=None, show_fig=False, weight_order=0): """Determine b/a ratio `q` and position angle `pa` of model by calculating its second moments. TODO: add weight function Parameters ---------- components: a list of `scarlet.Component` or `scarlet.ComponentTree` Component to analyze weight_order: W(x, y) = I(x, y) ** (weight_order) """ if not isinstance(components, list): components = [components] blend = scarlet.Blend(components, observation) model = blend.get_model() mask = (observation.weights == 0) model = model * ~mask if weight_order < 0: raise ValueError( 'Weight order cannot be negative, this will introduce infinity!') elif weight_order == 0: weight = None else: weight = model**weight_order # zeroth-order moment: total flux w00 = raw_moment(model, 0, 0, weight) # first-order moment: centroid w10 = raw_moment(model, 1, 0, weight) w01 = raw_moment(model, 0, 1, weight) x_c = w10 / w00 y_c = w01 / w00 # second-order moment: b/a ratio and position angle m11 = raw_moment(model, 1, 1, weight) / w00 - x_c * y_c m20 = raw_moment(model, 2, 0, weight) / w00 - x_c**2 m02 = raw_moment(model, 0, 2, weight) / w00 - y_c**2 cov = np.array([m20, m11, m11, m02]).T.reshape(-1, 2, 2) eigvals, eigvecs = np.linalg.eigh(cov) # q = b/a q = np.sqrt(np.min(eigvals, axis=1) / np.max(eigvals, axis=1)) # position angle PA: between the major axis and the east (positive x-axis) major_axis = eigvecs[np.arange(len(eigvecs)), np.argmax(eigvals, axis=1), :] sign = np.sign(major_axis[:, 1]) # sign of y-component pa = np.rad2deg(np.arccos(np.dot(major_axis, [1, 0]))) pa = np.array([x - 180 if abs(x) > 90 else x for x in pa]) pa *= sign if show_fig: fig, ax = plt.subplots() norm = scarlet.display.AsinhMapping(minimum=-0.1, stretch=0.5, Q=1) ax.imshow(scarlet.display.img_to_rgb(model, norm=norm)) def make_lines(eigvals, eigvecs, mean, i): """Make lines a length of 2 stddev.""" std = np.sqrt(eigvals[i]) vec = 1.5 * std * eigvecs[:, i] / np.hypot(*eigvecs[:, i]) x, y = np.vstack((mean - vec, mean, mean + vec)).T return x, y mean = np.array([x_c[0], y_c[0]]) ax.plot(*make_lines(eigvals[0], eigvecs[0], mean, 0), marker='o', color='blue', alpha=0.4) ax.plot(*make_lines(eigvals[0], eigvecs[0], mean, -1), marker='o', color='red', alpha=0.4) mean = np.array([x_c[2], y_c[2]]) ax.plot(*make_lines(eigvals[2], eigvecs[2], mean, 0), marker='o', color='blue', alpha=0.4) ax.plot(*make_lines(eigvals[2], eigvecs[2], mean, -1), marker='o', color='red', alpha=0.4) ax.axis('image') plt.show() return {'q': q, 'pa': pa}
def display_scarlet_model(blend, zoomin_size=None, ax=None, show_loss=False, show_mask=False, show_gray_mask=True, show_ind=None, add_boxes=True, stretch=2, Q=1, minimum=0.0, channels='grizy', show_mark=True, pixel_scale=0.168, scale_bar=True, scale_bar_length=5.0, scale_bar_fontsize=20, scale_bar_y_offset=0.5, scale_bar_color='w', scale_bar_loc='left', add_text=None, usetex=False, text_fontsize=30, text_y_offset=0.80, text_color='w'): ''' Display the scene, including image, mask, and the models. Arguments: blend (scarlet.blend): the blend of observation and sources zoomin_size (float, in arcsec): the size of shown image, if not showing in full size ax (matplotlib.axes object): input axes object show_loss (bool): whether displaying the loss curve show_mask (bool): whether displaying the mask encoded in `data.weights' show_ind (list): if not None, only objects with these indices are shown in the figure stretch, Q, minimum (float): parameters for displaying image, see https://pmelchior.github.io/scarlet/tutorials/display.html channels (str): names of the bands in `observation` show_mark (bool): whether plot the indices of sources in the figure pixel_scale (float): default is 0.168 arcsec/pixel Returns: ax: if input `ax` is provided fig: if no `ax` is provided as input ''' import scarlet from scarlet.display import AsinhMapping if ax is None: if show_loss: fig = plt.figure(figsize=(24, 6)) ax = [fig.add_subplot(1, 4, n + 1) for n in range(4)] else: fig = plt.figure(figsize=(18, 6)) ax = [fig.add_subplot(1, 3, n + 1) for n in range(3)] # Observation observation = blend.observations[0] loss = blend.loss # Sometimes we only want to show a few sources if show_ind is not None: sources = np.copy(blend.sources) gal_sources = np.array(sources)[show_ind] blend = scarlet.Blend(gal_sources, observation) if zoomin_size is not None: x_cen = observation.model_frame.shape[2] // 2 y_cen = observation.model_frame.shape[1] // 2 size = int(zoomin_size / pixel_scale / 2) # half-size # Image images = observation.data[:, y_cen - size:y_cen + size + 1, x_cen - size:x_cen + size + 1] # Weights weights = observation.weights[:, y_cen - size:y_cen + size + 1, x_cen - size:x_cen + size + 1] # Compute model model = blend.get_model()[:, y_cen - size:y_cen + size + 1, x_cen - size:x_cen + size + 1] # this model is under `model_frame`, i.e. under the modest PSF else: # Image images = observation.data # Weights weights = observation.weights # Compute model model = blend.get_model() # this model is under `model_frame`, i.e. under the modest PSF # Render it in the observed frame model_ = observation.render(model) # Mask mask = (np.sum(weights == 0, axis=0) != 0) # Compute residual residual = images - model_ # Create RGB images norm = AsinhMapping(minimum=minimum, stretch=stretch, Q=Q) img_rgb = scarlet.display.img_to_rgb(images, norm=norm) channel_map = scarlet.display.channels_to_rgb(len(channels)) model_rgb = scarlet.display.img_to_rgb(model_, norm=norm) norm = AsinhMapping(minimum=minimum, stretch=stretch, Q=Q / 2) residual_rgb = scarlet.display.img_to_rgb(residual, norm=norm, channel_map=channel_map) vmax = np.max(np.abs(residual_rgb)) # Show the data, model, and residual if show_mask: ax[0].imshow(img_rgb * (~np.tile(mask.T, (3, 1, 1))).T) ax[0].set_title("Data") ax[1].imshow(model_rgb * (~np.tile(mask.T, (3, 1, 1))).T) ax[1].set_title("Model") ax[2].imshow(residual_rgb * (~np.tile(mask.T, (3, 1, 1))).T, vmin=-vmax, vmax=vmax, cmap='seismic') ax[2].set_title("Residual") elif show_gray_mask: ax[0].imshow(img_rgb) ax[0].set_title("Data") ax[1].imshow(model_rgb) ax[1].set_title("Model") ax[2].imshow(residual_rgb, vmin=-vmax, vmax=vmax, cmap='seismic') ax[2].set_title("Residual") ax[0].imshow(mask.astype(float), origin='lower', alpha=0.1, cmap='Greys_r') ax[1].imshow(mask.astype(float), origin='lower', alpha=0.1, cmap='Greys_r') ax[2].imshow(mask.astype(float), origin='lower', alpha=0.1, cmap='Greys_r') else: ax[0].imshow(img_rgb) ax[0].set_title("Data") ax[1].imshow(model_rgb) ax[1].set_title("Model") ax[2].imshow(residual_rgb, vmin=-vmax, vmax=vmax, cmap='seismic') ax[2].set_title("Residual") if show_mark: for k, src in enumerate(blend.sources): if isinstance(src, scarlet.source.PointSource): color = 'white' elif isinstance(src, scarlet.source.CompactExtendedSource): color = 'yellow' elif isinstance(src, scarlet.source.SingleExtendedSource): color = 'red' elif isinstance(src, scarlet.source.MultiExtendedSource): color = 'cyan' elif isinstance(src, scarlet.source.StarletSource): color = 'lime' else: color = 'gray' if hasattr(src, "center"): y, x = src.center if zoomin_size is not None: y = y - y_cen + size x = x - x_cen + size ax[0].text(x, y, k, color=color) ax[0].text(x, y, '+', color=color, horizontalalignment='center', verticalalignment='center') ax[1].text(x, y, k, color=color) ax[1].text(x, y, '+', color=color, horizontalalignment='center', verticalalignment='center') ax[2].text(x, y, k, color=color) ax[2].text(x, y, '+', color=color, horizontalalignment='center', verticalalignment='center') (img_size_x, img_size_y) = images[0].shape if scale_bar: if scale_bar_loc == 'left': scale_bar_x_0 = int(img_size_x * 0.04) scale_bar_x_1 = int(img_size_x * 0.04 + (scale_bar_length / pixel_scale)) else: scale_bar_x_0 = int(img_size_x * 0.95 - (scale_bar_length / pixel_scale)) scale_bar_x_1 = int(img_size_x * 0.95) scale_bar_y = int(img_size_y * 0.10) scale_bar_text_x = (scale_bar_x_0 + scale_bar_x_1) / 2 scale_bar_text_y = (scale_bar_y * scale_bar_y_offset) if scale_bar_length < 60: scale_bar_text = r'$%d^{\prime\prime}$' % int(scale_bar_length) elif 60 < scale_bar_length < 3600: scale_bar_text = r'$%d^{\prime}$' % int(scale_bar_length / 60) else: scale_bar_text = r'$%d^{\circ}$' % int(scale_bar_length / 3600) scale_bar_text_size = scale_bar_fontsize ax[0].plot([scale_bar_x_0, scale_bar_x_1], [scale_bar_y, scale_bar_y], linewidth=3, c=scale_bar_color, alpha=1.0) ax[0].text(scale_bar_text_x, scale_bar_text_y, scale_bar_text, fontsize=scale_bar_text_size, horizontalalignment='center', color=scale_bar_color) if add_text is not None: text_x_0 = int(img_size_x * 0.08) text_y_0 = int(img_size_y * text_y_offset) if usetex: ax[0].text(text_x_0, text_y_0, r'$\mathrm{' + add_text + '}$', fontsize=text_fontsize, color=text_color) else: ax[0].text(text_x_0, text_y_0, add_text, fontsize=text_fontsize, color=text_color) if show_loss: ax[3].plot(-np.array(loss)) ax[3].set_xlabel('Iteration') ax[3].set_ylabel('log-Likelihood') ax[3].set_title("log-Likelihood") xlim, ylim = ax[3].axes.get_xlim(), ax[3].axes.get_ylim() xrange = xlim[1] - xlim[0] yrange = ylim[1] - ylim[0] ax[3].set_aspect((xrange / yrange), adjustable='box') ax[3].ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) if add_boxes: from matplotlib.patches import Rectangle for k, src in enumerate(blend.sources): box_kwargs = {"facecolor": "none", "edgecolor": "w", "lw": 0.5} if zoomin_size is not None: extent = get_extent(src.bbox, [x_cen - size, y_cen - size]) else: extent = get_extent(src.bbox) # print(extent) rect = Rectangle((extent[0], extent[2]), extent[1] - extent[0], extent[3] - extent[2], **box_kwargs) # print(rect) ax[0].add_patch(rect) # ax[1].add_patch(rect) # ax[2].add_patch(rect) # for panel in range(len(ax)): # ax[panel].add_patch(rect) from matplotlib.ticker import MaxNLocator, NullFormatter for axx in ax: axx.yaxis.set_major_locator(MaxNLocator(5)) axx.xaxis.set_major_locator(MaxNLocator(5)) # Show the size of PSF. FWHM is 1 arcsec. from matplotlib.patches import Circle circ1 = Circle((40, 40), radius=1 / 0.168, linewidth=1., hatch='/////', fc='None', ec='white') ax[1].add_patch(circ1) plt.subplots_adjust(wspace=0.2) if ax is None: return fig return ax
def Sersic_fitting(components, observation=None, file_dir='./Models/', prefix='LSBG', index=0, zeropoint=27.0, pixel_scale=0.168, save_fig=True): ''' Fit a single Sersic model to the rendered model. Using `pymfit` by Johnny Greco https://github.com/johnnygreco/pymfit ''' from .utils import save_to_fits from .display import display_pymfit_model import pymfit if not os.path.isdir(file_dir): os.mkdir(file_dir) if observation is None: raise ValueError('Please provide `Observation`.') if isinstance(components, scarlet.Component): # Single component model = components.get_model() else: # Multiple components blend = scarlet.Blend(components, observation) model = blend.get_model() # First, we measure the basic properties of the galaxy to get initial values for fitting measure_dict = makeMeasurement(components, observation, frac=0.5, zeropoint=zeropoint, pixel_scale=pixel_scale, weight_order=0) # Then, we save the scarlet model and the inverse-variance map into a FITS file img_fn = os.path.join(file_dir, f'{prefix}-{index:04d}-scarlet-model.fits') invvar_fn = os.path.join(file_dir, f'{prefix}-{index:04d}-scarlet-invvar.fits') _ = save_to_fits(model.mean(axis=0), img_fn) invvar = [] # Calculate inv-variance map for the scene if isinstance(components, scarlet.Component): # Single component src = components invvar.append( 1 / np.sum(list(map(lambda a: a.std.data**2, src.parameters[1::2])), axis=0)) # boxed inv-variance _ = save_to_fits(invvar[0], invvar_fn) else: # Multiple components for src in components: invvar.append(1 / np.sum(list( map(lambda a: 1 / a.std.data**2, src.parameters[1::2])), axis=0)) # boxed inv-variance slices = tuple((src._model_frame_slices[1:], src._model_slices[1:]) for src in components) # Inv-variance map in the full scene full_invvar = np.zeros(blend.frame.shape[1:], dtype=blend.frame.dtype) # the inv-variance of background is 0??? full_invvar = scarlet.blend._add_models(*invvar, full_model=full_invvar, slices=slices) _ = save_to_fits(full_invvar, invvar_fn) # Fit a Sersic model using `pymfit` # Initial params that are different from defaults. # syntax is {parameter: [value, low, high]} pa_init = - np.sign(measure_dict['pa'].mean()) * \ (90 - abs(measure_dict['pa'].mean())) init_params = dict( PA=[pa_init, -90, 90], n=[1.0, 0.01, 5.0], ) # e=[1 - measure_dict['q'].mean(), 0, 1]) # create a config dictionary config = pymfit.sersic_config(init_params, img_shape=img_fn) # run imfit # note that the image file is a multi-extension cube, which explains the '[#]' additions # also note that this config will be written to config_fn. if you already have a # config file written, then use config=None (default) and skip the above step. sersic = pymfit.run(img_fn, config_fn=os.path.join(file_dir, 'config.txt'), mask_fn=None, config=config, var_fn=invvar_fn, out_fn=os.path.join(file_dir, 'best-fit.dat'), weights=True) if isinstance(components, scarlet.Component): # Single component: sersic['X0_scene'] = sersic['X0'] + components.bbox.origin[2] sersic['Y0_scene'] = sersic['Y0'] + components.bbox.origin[1] w = observation.frame.wcs ra, dec = w.wcs_pix2world(sersic['X0_scene'], sersic['Y0_scene'], 0) sersic['RA0'] = float(ra) sersic['DEC0'] = float(dec) sersic_model = pymfit.Sersic(sersic) if save_fig: if isinstance(components, scarlet.Component): # Single component blend = scarlet.Blend([components], observation) display_pymfit_model(blend, sersic_model.params, figsize=(30, 6), cmap='Greys_r', colorbar=True, fontsize=17, show=False, save_fn=os.path.join( './Figures/', f'{prefix}-{index:04d}-Sersic.png')) # Make a dictionary containing non-parametric measurements and Sersci fitting results measurement = copy.deepcopy(measure_dict) for key in sersic_model.params.keys(): measurement['_'.join(['sersic', key])] = sersic_model.params[key] measurement['sersic_SB0'] = sersic_model.mu_0 measurement['sersic_SBe'] = sersic_model.mu_e measurement['sersic_mag'] = sersic_model.m_tot return sersic_model, measurement