Esempio n. 1
0
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
Esempio n. 2
0
    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
Esempio n. 3
0
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
Esempio n. 5
0
    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]
Esempio n. 6
0
    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)
Esempio n. 7
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
Esempio n. 8
0
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
Esempio n. 9
0
    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
Esempio n. 10
0
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
Esempio n. 11
0
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
Esempio n. 12
0
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
Esempio n. 14
0
    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())
Esempio n. 15
0
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
Esempio n. 16
0
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)
Esempio n. 17
0
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_)
Esempio n. 18
0
    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
Esempio n. 19
0
def flux_radius(components, observation=None, frac=0.5, weight_order=0):
    """
    Determine the radius R (in pixels, along semi-major axis), 
    the flux within R has a fraction of `frac` over the total flux.

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

    frac: float
        fraction of lights within this R.

    """
    from scipy.interpolate import interp1d, UnivariateSpline

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

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

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

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

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

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

    return np.array(r_frac)
Esempio n. 20
0
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}
Esempio n. 21
0
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
Esempio n. 22
0
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