Beispiel #1
0
def test_psf_gen_psf_wldeblend_ps_rng():
    rng = np.random.RandomState(seed=42)
    psf_config = {
        "type": "wldeblend-ps",
        "variation_factor": 8,
    }
    gs_config, psf = gen_psf(
        rng=rng, psf_config=psf_config, gal_config={"type": "lsst-riz"},
        se_image_shape=100, se_wcs=galsim.PixelScale(0.2),
    )
    gmod = psf.getPSF(galsim.PositionD(x=10, y=10))

    rng = np.random.RandomState(seed=42)
    psf_config = {
        "type": "wldeblend-ps",
        "variation_factor": 8,
    }
    gs_config1, psf1 = gen_psf(
        rng=rng, psf_config=psf_config, gal_config={"type": "lsst-riz"},
        se_image_shape=100, se_wcs=galsim.PixelScale(0.2),
    )
    gmod1 = psf1.getPSF(galsim.PositionD(x=10, y=10))

    assert gmod1 == gmod
    assert gs_config1 == gs_config

    assert gs_config["beta"] == 2.5
    assert gs_config["type"] == "Moffat"
    assert psf._variation_factor == 8
    assert psf._median_seeing == 0.85
Beispiel #2
0
    def _set_target_psf(self):
        # find the largets kernel according to
        #  sqrt(T) * dilation
        # where
        # g = sqrt(shear.g1**2 + shear.g2**2)
        # dilation = 1.0 + 2.0*g
        self._target_psf_image = None
        self._target_psf_size = None
        self._psf_im_shape = None
        ni = self.image_shape[0] // self.patch_size
        nj = self.image_shape[1] // self.patch_size
        dg = (self.patch_size - 1) / 2

        for i in range(ni + 1):
            row = min(i * self.patch_size + dg, self.image_shape[0])
            for j in range(nj + 1):
                col = min(j * self.patch_size + dg, self.image_shape[1])

                psf_im = self.psf_model(row, col)
                psf_im /= np.sum(psf_im)
                if self._psf_im_shape is None:
                    self._psf_im_shape = psf_im.shape
                    assert self._psf_im_shape[0] % 2 == 1
                    assert self._psf_im_shape[1] % 2 == 1
                    assert self._psf_im_shape[0] == self._psf_im_shape[1]
                else:
                    assert self._psf_im_shape == psf_im.shape

                hsmpars = galsim.hsm.HSMParams(max_mom2_iter=1000)
                gim = galsim.ImageD(psf_im, wcs=galsim.PixelScale(1))
                try:
                    moms = galsim.hsm.FindAdaptiveMom(gim, hsmparams=hsmpars)
                    fac = gim.calculateFWHM() * (1.0 + 2.0 * np.sqrt(
                        moms.observed_shape.g1**2 + moms.observed_shape.g2**2))
                    if (self._target_psf_size is None
                            or fac > self._target_psf_size):
                        self._target_psf_size = fac
                        self._target_psf_image = psf_im
                        self._target_psf_loc = (row, col)
                except galsim.errors.GalSimHSMError:
                    pass

        # the final PSF is an interpolated image convolved with the Gaussian
        # smoothing kernel
        self._target_psf = galsim.Convolve(
            galsim.InterpolatedImage(galsim.ImageD(self._target_psf_image),
                                     wcs=galsim.PixelScale(1)),
            galsim.Gaussian(sigma=self.sigma))
Beispiel #3
0
def test_render_source_in_image(x, y):
    source = galsim.Gaussian(fwhm=0.9)
    local_wcs = galsim.PixelScale(0.25)
    draw_method = 'auto'
    image_pos = galsim.PositionD(x=x, y=y)

    expected_shape = source.drawImage(wcs=local_wcs,
                                      method=draw_method).array.shape

    stamp = render_source_in_image(source=source,
                                   image_pos=image_pos,
                                   local_wcs=local_wcs,
                                   draw_method=draw_method)

    assert stamp.array.shape == expected_shape

    # any odd number works here for the image size - need to have image center
    # be a pixel center
    full_img = source.shift(dx=image_pos.x * 0.25,
                            dy=image_pos.y * 0.25).drawImage(wcs=local_wcs,
                                                             nx=187,
                                                             ny=187)
    # since the shift method offsets from image center, we set
    # image center to (0, 0) so the object shifts match the expected pixel
    # location
    full_img.setCenter(0, 0)

    # now the bounds of the stamp are in the same coords as the full image
    # so just grab that subset and compare
    assert np.allclose(full_img[stamp.bounds].array,
                       stamp.array,
                       rtol=0,
                       atol=5e-7)
Beispiel #4
0
    def getPSF(self, image_pos, pixel_scale=None, gsparams=None):
        """Returns the PSF at position image_pos

        @param image_pos    The position in image coordinates at which to build the PSF.
        @param gsparams     (Optional) A GSParams instance to pass to the constructed GSObject.
        @param pixel_scale  A deprecated parameter that is only present for backwards compatibility.
                            If the constructor did not provide an image file or wcs, then 
                            this will use the pixel scale for an approximate wcs.

        @returns the PSF as a GSObject
        """
        # Build an image version of the numpy array
        im = galsim.Image(self.getPSFArray(image_pos))

        # Build the PSF profile in the image coordinate system.
        psf = galsim.InterpolatedImage(im,
                                       scale=self.sample_scale,
                                       flux=1,
                                       x_interpolant=galsim.Lanczos(3),
                                       gsparams=gsparams)

        # This brings if from image coordinates to world coordinates.
        if self.wcs:
            psf = self.wcs.toWorld(psf, image_pos=image_pos)
        elif pixel_scale:
            depr(
                'pixel_scale', 1.1,
                'wcs=PixelScale(pixel_scale) in the constructor for DES_PSFEx')
            psf = galsim.PixelScale(pixel_scale).toWorld(psf)

        return psf
Beispiel #5
0
def BuildWCS(config):
    """Read the wcs parameters from the config dict and return a constructed wcs object.
    """
    image = config['image']

    # If there is a wcs field, read it and update the wcs variable.
    if 'wcs' in image:
        image_wcs = image['wcs']
        if 'type' in image_wcs:
            wcs_type = image_wcs['type']
        else:
            wcs_type = 'PixelScale'

        # Special case: origin == center means to use image_center for the wcs origin
        if 'origin' in image_wcs and image_wcs['origin'] == 'center':
            origin = config['image_center']
            image_wcs['origin'] = origin

        if wcs_type not in valid_wcs_types:
            raise AttributeError("Invalid image.wcs.type=%s." % wcs_type)

        builder = valid_wcs_types[wcs_type]
        wcs = builder.buildWCS(image_wcs, config)

    else:
        # Default if no wcs is to use PixelScale
        if 'pixel_scale' in image:
            scale = galsim.config.ParseValue(image, 'pixel_scale', config,
                                             float)[0]
        else:
            scale = 1.0
        wcs = galsim.PixelScale(scale)

    return wcs
Beispiel #6
0
    def getKwargs(self, config, base, logger):
        req = {'file_name': str}
        opt = {'dir': str, 'image_file_name': str}
        kwargs, safe = galsim.config.GetAllParams(config,
                                                  base,
                                                  req=req,
                                                  opt=opt)

        if 'image_file_name' not in kwargs:
            if 'wcs' in base:
                wcs = base['wcs']
                if wcs.isLocal():
                    # Then the wcs is already fine.
                    pass
                elif 'image_pos' in base:
                    image_pos = base['image_pos']
                    wcs = wcs.local(image_pos)
                    safe = False
                else:
                    raise RuntimeError(
                        "No image_pos found in config, but wcs is not local.")
                kwargs['wcs'] = wcs
            else:
                # Then we aren't doing normal config processing, so just use pixel scale = 1.
                kwargs['wcs'] = galsim.PixelScale(1.)

        return kwargs, safe
Beispiel #7
0
def test_render_sources_for_image():
    image_shape = (64, 64)
    wcs = galsim.PixelScale(0.25)
    draw_method = 'auto'
    src_inds = np.arange(5)

    def src_func(ind):
        obj = galsim.Gaussian(fwhm=ind * 0.1 + 0.9)
        rng = np.random.RandomState(seed=ind + 1)
        image_pos = galsim.PositionD(
            x=rng.uniform(low=20, high=40),
            y=rng.uniform(low=20, high=40),
        )
        return obj, image_pos

    im = render_sources_for_image(image_shape=image_shape,
                                  wcs=wcs,
                                  draw_method=draw_method,
                                  src_inds=src_inds,
                                  src_func=src_func,
                                  n_jobs=1)

    # now render them via direct sum of objects
    objs = []
    for ind in src_inds:
        obj, pos = src_func(ind)
        objs.append(obj.shift(dx=pos.x * 0.25, dy=pos.y * 0.25))
    im_sum = galsim.Sum(objs).drawImage(wcs=wcs, nx=155, ny=155)
    # we set the center to (0, 0) so that the offsets from the center
    # match the coords of the objects
    im_sum.setCenter(0, 0)

    assert np.allclose(im_sum[im.bounds].array, im.array, rtol=0, atol=5e-7)
Beispiel #8
0
 def check_symm_noise(noise_image, msg):
     # A helper funciton to see if a noise image has 4-fold symmetric noise.
     im2 = noise_image.copy()
     # Clear out any wcs to make the test simpler
     im2.wcs = galsim.PixelScale(1.)
     noise = galsim.CorrelatedNoise(im2)
     cf = noise.drawImage(galsim.Image(bounds=galsim.BoundsI(-1,1,-1,1), scale=1))
     # First check the variance
     print('variance: ',cf(0,0), noise.getVariance())
     np.testing.assert_almost_equal(cf(0,0)/noise.getVariance(), 1.0, decimal=VAR_NDECIMAL,
                                    err_msg=msg + ':: noise variance is wrong.')
     cf_plus = np.array([ cf(1,0), cf(-1,0), cf(0,1), cf(0,-1) ])
     cf_cross = np.array([ cf(1,1), cf(-1,-1), cf(-1,1), cf(1,-1) ])
     print('plus pattern: ',cf_plus)
     print('diff relative to dc: ',(cf_plus-np.mean(cf_plus))/cf(0,0))
     print('cross pattern: ',cf_cross)
     print('diff relative to dc: ',(cf_cross-np.mean(cf_cross))/cf(0,0))
     # For now, don't make these asserts.  Just print whether they will pass or fail.
     if True:
         if np.all(np.abs((cf_plus-np.mean(cf_plus))/cf(0,0)) < 0.01):
             print('plus test passes')
         else:
             print('*** FAIL ***')
             print(msg + ': plus pattern is not constant')
         if np.all(np.abs((cf_cross-np.mean(cf_cross))/cf(0,0)) < 0.01):
             print('cross test passes')
         else:
             print('*** FAIL ***')
             print(msg + ': cross pattern is not constant')
     else:
         np.testing.assert_almost_equal((cf_plus-np.mean(cf_plus))/cf(0,0), 0.0, decimal=2,
                                        err_msg=msg + ': plus pattern is not constant')
         np.testing.assert_almost_equal((cf_cross-np.mean(cf_cross))/cf(0,0), 0.0, decimal=2,
                                        err_msg=msg + ': cross pattern is not constant')
Beispiel #9
0
    def get_kernel(self, row, col, sampling_factor=1):
        """Get an image of the homogenization kernel.

        This kernel should not be used directly. This method is only
        for visualization purposes.

        Parameters
        ----------
        sampling_factor : float, optional
            A sampling factor by which to oversample the kernel image. A
            sampling factor of 2 indicates that the output image pixels
            are half the size of the original image pixels.

        Returns
        -------
        kern : np.ndarray
            An image of the homogenization kernel.
        """
        psf_im = self.psf_model(row, col)
        psf_im /= np.sum(psf_im)
        gim = galsim.InterpolatedImage(galsim.ImageD(psf_im),
                                       wcs=galsim.PixelScale(1))
        kern = galsim.Convolve(self._target_psf, galsim.Deconvolve(gim))
        return kern.drawImage(nx=self._psf_im_shape[1],
                              ny=self._psf_im_shape[0],
                              scale=1.0 / sampling_factor,
                              method='no_pixel').array
Beispiel #10
0
def test_render_sim_smoke():
    img_dim = 103
    img_cen = (img_dim - 1)/2
    scale = 0.25
    method = 'auto'
    g1 = 0.0
    g2 = 0.0
    shear_scene = False

    objs = [galsim.Exponential(half_light_radius=0.5)]

    def _psf_function(*, x, y):
        assert np.allclose(x, img_cen)
        assert np.allclose(y, img_cen)
        return galsim.Gaussian(fwhm=0.9)

    uv_offsets = [galsim.PositionD(x=0.0, y=0.0)]
    uv_cen = galsim.PositionD(x=img_cen * scale, y=img_cen * scale)
    wcs = galsim.PixelScale(scale)

    se_img = render_objs_with_psf_shear(
        objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets,
        uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method,
        g1=g1, g2=g2, shear_scene=shear_scene)

    expected_img = galsim.Convolve(
        objs[0], galsim.Gaussian(fwhm=0.9)
    ).drawImage(
        nx=img_dim, ny=img_dim, scale=scale)

    assert np.allclose(expected_img.array, se_img.array, rtol=0, atol=1e-6)
def test_mask_and_apodize_stars():
    rng = np.random.RandomState(seed=11)
    stars = np.array([(75, 85, 20)],
                     dtype=[("x", "f8"), ("y", "f8"), ("radius_pixels", "f8")])
    dim = 215

    cdata = {
        "image": rng.normal(size=(dim, dim)),
        "weight": np.ones((dim, dim)),
        "mfrac": np.zeros((dim, dim)),
        "ormask": np.zeros((dim, dim), dtype=np.int32),
        "bmask": np.zeros((dim, dim), dtype=np.int32),
        "noise": rng.normal(size=(dim, dim)),
        "psf": rng.normal(size=(dim, dim)),
    }

    mbobs = make_mbobs_from_coadd_data(
        wcs=galsim.PixelScale(0.263).jacobian(),
        cdata=cdata,
    )

    mbobs_old = copy.deepcopy(mbobs)

    rng = np.random.RandomState(seed=10)
    mask_stars(
        rng=rng,
        mbobs=mbobs,
        stars=stars,
        interp_cfg={
            "skip": True,
            "iso_buff": 1,
            "fill_isolated_with_noise": False
        },
        apodize_cfg={
            "skip": False,
            "ap_rad": 1
        },
        mask_expand_rad=0,
    )

    # check something changed
    for obslist, obslist_old in zip(mbobs, mbobs_old):
        for obs, obs_old in zip(obslist, obslist_old):
            for attr in ["image", "bmask", "noise", "weight", "mfrac"]:
                assert not np.array_equal(getattr(obs, attr),
                                          getattr(obs_old, attr))

    # check some sentinel values
    assert np.all(mbobs[0][0].weight[80:80, 70:80] == 0)
    assert np.all(mbobs[0][0].mfrac[80:80, 70:80] == 1)
    assert np.all((mbobs[0][0].bmask[80:80, 70:80] & BMASK_GAIA_STAR) != 0)
    assert np.all((mbobs[0][0].bmask[80:80, 70:80] & BMASK_SPLINE_INTERP) == 0)

    # but not everything ofc
    assert not np.all(mbobs[0][0].weight == 0)
    assert not np.all(mbobs[0][0].mfrac == 1)
    assert not np.all((mbobs[0][0].bmask & BMASK_GAIA_STAR) != 0)
    assert np.all((mbobs[0][0].bmask & BMASK_SPLINE_INTERP) == 0)
Beispiel #12
0
def test_gauss_pix_psf_reproducible():
    wcs = galsim.PixelScale(0.5)
    psf_model = GaussPixPSF(s2n=100)
    psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs)
    psf_im1 = psf.drawImage(nx=53, ny=53, scale=0.263).array

    psf_model = GaussPixPSF(s2n=100)
    psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs)
    psf_im2 = psf.drawImage(nx=53, ny=53, scale=0.263).array

    assert np.allclose(psf_im1, psf_im2)
Beispiel #13
0
    def getKwargs(self, config, base, logger):
        req = { 'file_name' : str }
        opt = { 'dir' : str, 'image_file_name' : str }
        kwargs, safe = galsim.config.GetAllParams(config, base, req=req, opt=opt)

        if 'image_file_name' not in kwargs:
            if 'wcs' in base:
                kwargs['wcs'] = base['wcs']
            else:
                # Then we aren't doing normal config processing, so just use pixel scale = 1.
                kwargs['wcs'] = galsim.PixelScale(1.)

        return kwargs, safe
Beispiel #14
0
    def _draw(self, image_pos, x_interpolant='lanczos15', gsparams=None):
        """Get an image of the PSF at the given location.

        Parameters
        ----------
        image_pos : galsim.Position
            The image position for the PSF.
        wcs : galsim.BaseWCS or subclass
            The WCS to use to draw the PSF.
        x_interpolant : str, optional
            The interpolant to use.
        gsparams : galsim.GSParams, optional
            Ootional galsim configuration data to pass along.

        Returns
        -------
        psf : galsim.InterpolatedImage
            The PSF at the image position.
        """
        scale = 0.25
        pixel_wcs = galsim.PixelScale(scale)

        # nice and big image size here cause this has been a problem
        image = galsim.ImageD(ncol=19, nrow=19, wcs=pixel_wcs)

        # piff offsets the center of the PSF from the true image
        # center - here we will return a properly centered image by undoing
        # the offset
        dx = image_pos.x - int(image_pos.x + 0.5)
        dy = image_pos.y - int(image_pos.y + 0.5)

        psf = self.getPiff().draw(
            image_pos.x,
            image_pos.y,
            image=image,
            offset=(-dx, -dy))

        psf = galsim.InterpolatedImage(
            galsim.ImageD(psf.array),  # make sure galsim is not keeping state
            wcs=pixel_wcs,
            gsparams=gsparams,
            x_interpolant=x_interpolant
        )

        psf = galsim.Convolve(
            [psf, galsim.Deconvolve(galsim.Pixel(scale))]
        ).withFlux(
            1.0
        )

        return psf
Beispiel #15
0
def make_objs(
    *,
    rng,
    blend,
    shear,
    top,
    hlr=1.0,
    flux=1e4,
    psf_fhwm=0.9,
    wcs_scale=0.2,
    npix=251,
    noise=1,
):
    if blend:
        if top:
            theta = np.random.uniform() * 2.0 * np.pi
            dx_pixels = 16 * np.cos(theta)
            dy_pixels = 16 * np.sin(theta)
        else:
            dx_pixels = 16
            dy_pixels = 0
    else:
        dx_pixels = 63
        dy_pixels = 63

    wcs = galsim.PixelScale(wcs_scale)
    psf = galsim.Gaussian(fwhm=psf_fhwm)
    dx = dx_pixels * wcs.scale
    dy = dy_pixels * wcs.scale

    dx1 = dx + rng.uniform(low=-0.5, high=0.5) * wcs.scale
    dy1 = dy + rng.uniform(low=-0.5, high=0.5) * wcs.scale
    dx2 = rng.uniform(low=-0.5, high=0.5) * wcs.scale
    dy2 = rng.uniform(low=-0.5, high=0.5) * wcs.scale

    cen = (npix - 1) / 2

    cen1 = (cen - dx1 / wcs.scale, cen - dy1 / wcs.scale)
    cen2 = (cen - dx2 / wcs.scale, cen - dy2 / wcs.scale)

    obj1 = galsim.Exponential(half_light_radius=hlr).shear(
        g1=shear, g2=0).shift(dx=-dx1, dy=-dy1).withFlux(flux)
    obj2 = galsim.Exponential(half_light_radius=hlr).shift(
        dx=-dx2, dy=-dy2).withFlux(flux)

    im = galsim.Convolve(obj1 + obj2, psf).drawImage(wcs=wcs, nx=npix,
                                                     ny=npix).array
    im += rng.normal(size=im.shape) * noise

    return im, psf, wcs, cen1, cen2, obj1, obj2, noise
Beispiel #16
0
def test_gauss_pix_psf_s2n():
    wcs = galsim.PixelScale(0.263)
    psf_model = GaussPixPSF(s2n=100)
    psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs)
    psf_im = psf.drawImage(nx=53, ny=53, scale=0.263).array

    seed = 4098
    rng = np.random.RandomState(seed=seed)
    g1 = rng.normal() * 0.01
    g2 = rng.normal() * 0.01
    fwhm = (rng.uniform(low=-0.1, high=0.1) + 1.0) * 0.9
    gs = galsim.Gaussian(fwhm=fwhm).shear(g1=g1, g2=g2).withFlux(1.0)
    test_im = gs.drawImage(nx=53, ny=53, scale=0.263).array

    noise_std = np.sqrt(np.sum(test_im**2) / 100**2)
    nse = np.std(psf_im - test_im)
    assert nse < noise_std
    assert nse > 0
Beispiel #17
0
def ParseWorldPos(config, param_name, base, logger):
    """A helper function to parse the 'world_pos' value.

    The world_pos can be specified either as a regular RA, Dec (which in GalSim is known as a
    CelestialCoord) or as Euclidean coordinates in the local tangent plane relative to the
    image center (a PositionD).

    1. For the RA/Dec option, the world_pos field should use the type RADec, which includes two
       values named ra and dec, each of which should be an Angle type.  e.g.::

            world_pos:
                type : RADec
                ra : 37 hours
                dec: -23 degrees

       Technically, any other type that results in a CelestialCoord is valid, but RADec is the
       only one that is defined natively in GalSim.

    2. For the relative position in the local tangent plane (where 0,0 is the position of the
       image center), you can use any PositionD type.  e.g.::

            world_pos:
                type : RandomCircle
                radius : 12       # arcsec
                inner_radius : 3  # arcsec

    Parameters:
        config:     The configuration dict for the stamp field.
        param_name: The name of the field in the config dict to parse as a world_pos.
                    Normally, this is just 'world_pos'.
        base:       The base configuration dict.

    Returns:
        either a CelestialCoord or a PositionD instance.
    """
    param = config[param_name]
    wcs = base.get(
        'wcs', galsim.PixelScale(1.0))  # should be here, but just in case...
    if wcs.isCelestial():
        return galsim.config.ParseValue(config, param_name, base,
                                        galsim.CelestialCoord)[0]
    else:
        return galsim.config.ParseValue(config, param_name, base,
                                        galsim.PositionD)[0]
Beispiel #18
0
def test_render_sim_shear_scene(shear_scene):
    img_dim = 103
    img_cen = (img_dim - 1)/2
    scale = 0.25
    method = 'auto'
    g1 = 0.5
    g2 = -0.2

    objs = [galsim.Exponential(half_light_radius=5.5)]

    def _psf_function(*, x, y):
        return galsim.Gaussian(fwhm=0.9)

    uv_offsets = [galsim.PositionD(x=-1.3, y=0.578)]
    uv_cen = galsim.PositionD(x=img_cen * scale, y=img_cen * scale)
    wcs = galsim.PixelScale(scale)

    se_img = render_objs_with_psf_shear(
        objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets,
        uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method,
        g1=g1, g2=g2, shear_scene=not shear_scene)

    se_img_shear_scene = render_objs_with_psf_shear(
        objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets,
        uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method,
        g1=g1, g2=g2, shear_scene=shear_scene)

    if shear_scene:
        expected_img = galsim.Convolve(
            objs[0].shift(uv_offsets[0]).shear(g1=g1, g2=g2),
            galsim.Gaussian(fwhm=0.9)
        ).drawImage(
            nx=img_dim, ny=img_dim, wcs=wcs.local(world_pos=uv_cen))
    else:
        expected_img = galsim.Convolve(
            objs[0].shear(g1=g1, g2=g2).shift(uv_offsets[0]),
            galsim.Gaussian(fwhm=0.9)
        ).drawImage(
            nx=img_dim, ny=img_dim, wcs=wcs.local(world_pos=uv_cen))

    assert not np.allclose(expected_img.array, se_img.array, rtol=0, atol=1e-9)
    assert np.allclose(
        expected_img.array, se_img_shear_scene.array, rtol=0, atol=1e-9)
Beispiel #19
0
def test_shear_position_image_integration_pixelwcs():
    wcs = galsim.PixelScale(0.3)
    obj1 = galsim.Gaussian(sigma=3)
    obj2 = galsim.Gaussian(sigma=2)
    pos2 = galsim.PositionD(3, 5)
    sum = obj1 + obj2.shift(pos2)
    shear = galsim.Shear(g1=0.1, g2=0.18)
    im1 = galsim.Image(50, 50, wcs=wcs)
    sum.shear(shear).drawImage(im1, center=im1.center)

    # Equivalent to shear each object separately and drawing at the sheared position.
    im2 = galsim.Image(50, 50, wcs=wcs)
    obj1.shear(shear).drawImage(im2, center=im2.center)
    obj2.shear(shear).drawImage(
        im2,
        add_to_image=True,
        center=im2.center + wcs.toImage(pos2.shear(shear)),
    )

    print("err:", np.max(np.abs(im1.array - im2.array)))
    assert np.allclose(im1.array, im2.array, rtol=0, atol=5e-8)
Beispiel #20
0
    def view(self, scale=None, wcs=None, origin=None, center=None, make_const=False):
        """Make a view of this image, which lets you change the scale, wcs, origin, etc.
        but view the same underlying data as the original image.

        If you do not provide either `scale` or `wcs`, the view will keep the same wcs
        as the current Image object.

        @param scale        If provided, use this as the pixel scale for the image. [default: None]
        @param wcs          If provided, use this as the wcs for the image. [default: None]
        @param origin       If profided, use this as the origin position of the view.
                            [default: None]
        @param center       If profided, use this as the center position of the view.
                            [default: None]
        @param make_const   Make the view's data array immutable. [default: False]
        """
        if origin is not None and center is not None:
            raise TypeError("Cannot provide both center and origin")

        if scale is not None:
            if wcs is not None:
                raise TypeError("Cannot provide both scale and wcs")
            wcs = galsim.PixelScale(scale)
        elif wcs is not None:
            if not isinstance(wcs,galsim.BaseWCS):
                raise TypeError("wcs parameters must be a galsim.BaseWCS instance")
        else:
            wcs = self.wcs

        if make_const:
            ret = Image(image=_galsim.ConstImageView[self.dtype](self.image.view()), wcs=wcs)
        else:
            ret = Image(image=self.image.view(), wcs=wcs)

        if origin is not None:
            ret.setOrigin(origin)
        elif center is not None:
            ret.setCenter(center)

        return ret
def test_mask_stars_raises():
    rng = np.random.RandomState(seed=11)
    stars = np.array([(75, 85, 20)],
                     dtype=[("x", "f8"), ("y", "f8"), ("radius_pixels", "f8")])
    dim = 215

    cdata = {
        "image": rng.normal(size=(dim, dim)),
        "weight": np.ones((dim, dim)),
        "mfrac": np.zeros((dim, dim)),
        "ormask": np.zeros((dim, dim), dtype=np.int32),
        "bmask": np.zeros((dim, dim), dtype=np.int32),
        "noise": rng.normal(size=(dim, dim)),
        "psf": rng.normal(size=(dim, dim)),
    }

    mbobs = make_mbobs_from_coadd_data(
        wcs=galsim.PixelScale(0.263).jacobian(),
        cdata=cdata,
    )

    rng = np.random.RandomState(seed=10)
    with pytest.raises(RuntimeError):
        mask_stars(
            rng=rng,
            mbobs=mbobs,
            stars=stars,
            interp_cfg={
                "skip": False,
                "iso_buff": 1,
                "fill_isolated_with_noise": False
            },
            apodize_cfg={
                "skip": False,
                "ap_rad": 1
            },
            mask_expand_rad=0,
        )
Beispiel #22
0
def test_exceptions():
    """Test failure modes for InterpolatedImage class.
    """
    import time
    t1 = time.time()

    try:
        # What if it receives as input something that is not an Image? Give it a GSObject to check.
        g = galsim.Gaussian(sigma=1.)
        np.testing.assert_raises(ValueError, galsim.InterpolatedImage, g)
        # What if Image does not have a scale set, but scale keyword is not specified?
        im = galsim.ImageF(5, 5)
        np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im)
        # Image must have bounds defined
        im = galsim.ImageF()
        im.scale = 1.
        np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im)
        # Weird flux normalization
        im = galsim.ImageF(5, 5, scale=1.)
        np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im, normalization = 'foo')
        # scale and WCS
        np.testing.assert_raises(TypeError, galsim.InterpolatedImage, im,
                                 wcs = galsim.PixelScale(1.), scale=1.)
        # weird WCS
        np.testing.assert_raises(TypeError, galsim.InterpolatedImage, im,
                                 wcs = 1.)
        # Weird interpolant - give it something random like a GSObject
        np.testing.assert_raises(Exception, galsim.InterpolatedImage, im, x_interpolant = g)
        # Image has wrong type
        im = galsim.ImageI(5, 5)
        np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im)
    except ImportError:
        print 'The assert_raises tests require nose'

    t2 = time.time()
    print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_coadd_image_correct(crazy_wcs, crazy_obj):

    rng = np.random.RandomState(seed=42)

    n_coadd = 10
    psf_dim = 51
    coadd_dim = 53

    coadd_cen = (coadd_dim + 1) / 2

    se_dim = int(np.ceil(coadd_dim * np.sqrt(2)))
    if se_dim % 2 == 0:
        se_dim += 1

    se_cen = (se_dim + 1) / 2
    scale = 0.2
    noise_std = 0.1
    world_origin = galsim.CelestialCoord(0 * galsim.degrees,
                                         0 * galsim.degrees)

    aff = galsim.PixelScale(scale).affine()
    aff = aff.withOrigin(galsim.PositionD(coadd_cen, coadd_cen),
                         galsim.PositionD(0, 0))
    coadd_wcs = galsim.TanWCS(
        aff,
        world_origin,
    )

    def _gen_psf_func(wcs, fwhm):
        def _psf_func(*args, **kargs):
            return galsim.Gaussian(fwhm=fwhm).drawImage(
                nx=101, ny=101,
                wcs=wcs.local(world_pos=world_origin)), galsim.PositionD(0, 0)

        return _psf_func

    wgts = []
    objs = []
    psf_objs = []
    exps = []
    for _ in range(n_coadd):
        if crazy_obj:
            _fwhm = 2.9 * (1.0 + rng.normal() * 0.1)
            _g1 = rng.normal() * 0.3
            _g2 = rng.normal() * 0.3
            obj = galsim.Gaussian(fwhm=_fwhm).shear(g1=_g1, g2=_g2)
        else:
            obj = galsim.Gaussian(fwhm=2.9).shear(g1=-0.1, g2=0.3)

        objs.append(obj)

        if crazy_wcs:
            shear = galsim.Shear(g1=rng.normal() * 0.01,
                                 g2=rng.normal() * 0.01)
            aff = galsim.ShearWCS(scale, shear).affine()
            aff = aff.withOrigin(galsim.PositionD(se_cen, se_cen),
                                 galsim.PositionD(0, 0))
            wcs = galsim.TanWCS(
                aff,
                world_origin,
            )
        else:
            aff = galsim.PixelScale(scale).affine()
            aff = aff.withOrigin(galsim.PositionD(se_cen, se_cen),
                                 galsim.PositionD(0, 0))
            wcs = galsim.TanWCS(
                aff,
                world_origin,
            )

        _noise = noise_std * (1 + (rng.uniform() - 0.5) * 2 * 0.05)
        wgts.append(1.0 / _noise**2)

        bmsk = galsim.ImageI(np.zeros((se_dim, se_dim)))

        img = obj.drawImage(
            nx=se_dim,
            ny=se_dim,
            wcs=wcs.local(world_pos=world_origin),
        )

        if crazy_obj:
            _psf_fwhm = 1.0 * (1.0 + rng.normal() * 0.1)
        else:
            _psf_fwhm = 1.0

        psf = galsim.Gaussian(fwhm=_psf_fwhm)
        psf_objs.append(psf)

        exp = make_exp(
            gsimage=img,
            bmask=bmsk,
            noise=_noise,
            galsim_wcs=wcs,
            galsim_psf=psf,
            psf_dim=psf_dim,
        )
        exps.append(exp)

    coadd_bbox = geom.Box2I(
        geom.IntervalI(min=0, max=coadd_dim - 1),
        geom.IntervalI(min=0, max=coadd_dim - 1),
    )
    coadd, exp_info = make_coadd_obs(
        exps=exps,
        coadd_wcs=make_dm_wcs(coadd_wcs),
        coadd_bbox=coadd_bbox,
        psf_dims=(psf_dim, ) * 2,
        rng=rng,
        remove_poisson=False,
    )

    coadd_img = coadd.image
    coadd_psf = coadd.psf.image

    wgts = np.array(wgts) / np.sum(wgts)
    true_coadd_img = galsim.Sum([
        obj.withFlux(wgt) for obj, wgt in zip(objs, wgts)
    ]).drawImage(nx=coadd_dim,
                 ny=coadd_dim,
                 wcs=coadd_wcs.local(world_pos=world_origin)).array

    true_coadd_psf = galsim.Sum([
        obj.withFlux(wgt) for obj, wgt in zip(psf_objs, wgts)
    ]).drawImage(nx=psf_dim,
                 ny=psf_dim,
                 wcs=coadd_wcs.local(world_pos=world_origin)).array

    if not crazy_wcs:
        rtol = 0
        atol = 5e-7
    else:
        rtol = 0
        atol = 5e-5

    coadd_img_err = np.max(np.abs(coadd_img - true_coadd_img))
    coadd_psf_err = np.max(np.abs(coadd_psf - true_coadd_psf))
    print("image max abs error:", coadd_img_err)
    print("psf max abs error:", coadd_psf_err)

    if not np.allclose(coadd_img, true_coadd_img, rtol=rtol, atol=atol):
        _plot_cmp(coadd_img, true_coadd_img, rtol, atol, crazy_obj, crazy_wcs,
                  "img")

    if not np.allclose(coadd_psf, true_coadd_psf, rtol=rtol, atol=atol):
        _plot_cmp(coadd_psf, true_coadd_psf, rtol, atol, crazy_obj, crazy_wcs,
                  "psf")

    assert np.allclose(coadd_img, true_coadd_img, rtol=rtol, atol=atol)
    assert np.allclose(coadd_psf, true_coadd_psf, rtol=rtol, atol=atol)
    assert np.all(np.isfinite(coadd.noise))
Beispiel #24
0
    def determinePsf(self,
                     exposure,
                     psfCandidateList,
                     metadata=None,
                     flagKey=None):
        """Determine a Piff PSF model for an exposure given a list of PSF
        candidates.

        Parameters
        ----------
        exposure : `lsst.afw.image.Exposure`
           Exposure containing the PSF candidates.
        psfCandidateList : `list` of `lsst.meas.algorithms.PsfCandidate`
           A sequence of PSF candidates typically obtained by detecting sources
           and then running them through a star selector.
        metadata : `lsst.daf.base import PropertyList` or `None`, optional
           A home for interesting tidbits of information.
        flagKey : `str` or `None`, optional
           Schema key used to mark sources actually used in PSF determination.

        Returns
        -------
        psf : `lsst.meas.extensions.piff.PiffPsf`
           The measured PSF model.
        psfCellSet : `None`
           Unused by this PsfDeterminer.
        """
        stars = []
        for candidate in psfCandidateList:
            cmi = candidate.getMaskedImage()
            weight = computeWeight(cmi, self.config.maxSNR)

            bbox = cmi.getBBox()
            bds = galsim.BoundsI(galsim.PositionI(*bbox.getMin()),
                                 galsim.PositionI(*bbox.getMax()))
            gsImage = galsim.Image(bds, scale=1.0, dtype=float)
            gsImage.array[:] = cmi.image.array
            gsWeight = galsim.Image(bds, scale=1.0, dtype=float)
            gsWeight.array[:] = weight

            source = candidate.getSource()
            image_pos = galsim.PositionD(source.getX(), source.getY())

            data = piff.StarData(gsImage, image_pos, weight=gsWeight)
            stars.append(piff.Star(data, None))

        kernelSize = int(
            np.clip(self.config.kernelSize, self.config.kernelSizeMin,
                    self.config.kernelSizeMax))

        piffConfig = {
            'type': "Simple",
            'model': {
                'type': 'PixelGrid',
                'scale': self.config.samplingSize,
                'size': kernelSize
            },
            'interp': {
                'type': 'BasisPolynomial',
                'order': self.config.spatialOrder
            },
            'outliers': {
                'type': 'Chisq',
                'nsigma': self.config.outlierNSigma,
                'max_remove': self.config.outlierMaxRemove
            }
        }

        piffResult = piff.PSF.process(piffConfig)
        # Run on a single CCD, and in image coords rather than sky coords.
        wcs = {0: galsim.PixelScale(1.0)}
        pointing = None

        logger = logging.getLogger(self.log.getName() + ".Piff")
        logger.addHandler(lsst.log.LogHandler())

        piffResult.fit(stars, wcs, pointing, logger=logger)
        psf = PiffPsf(kernelSize, kernelSize, piffResult)

        used_image_pos = [s.image_pos for s in piffResult.stars]
        if flagKey:
            for candidate in psfCandidateList:
                source = candidate.getSource()
                posd = galsim.PositionD(source.getX(), source.getY())
                if posd in used_image_pos:
                    source.set(flagKey, True)

        if metadata is not None:
            metadata.set("spatialFitChi2", piffResult.chisq)
            metadata.set("numAvailStars", len(stars))
            metadata.set("numGoodStars", len(piffResult.stars))
            metadata.set("avgX", np.mean([p.x for p in piffResult.stars]))
            metadata.set("avgY", np.mean([p.y for p in piffResult.stars]))

        return psf, None
Beispiel #25
0
def get_blend_shape(mu,
                    c,
                    e1,
                    e2,
                    hlr,
                    flux,
                    hsm=HLRShearModel(),
                    wcs=None,
                    pixel_scale=None,
                    return_hlr=False,
                    return_moments=False,
                    out_unit='pixel'):
    """        
    Returns the combined shear of the blended system
    
    Not quite ready for multiple blended systems! TODO!

    [assuming N galaxies in the blended system]

    #A  : the array of total NORMALIZED fluxes
    mu : the array (N vectors) of galaxy centers (i.e. the peaks of the Gaussians)
    c  : the vector pointing to the luminosity center of the blended system
    e1 : the array of the first component of the shears for N galaxies 
    e2 : the array of the second component of the shears for N galaxies
    flux : the flux of galaxies in the blend (in whatever unit or zeropoint but consistent) 
    hlr in arcsec and mu,c in degrees.
    returns Q_blend in pixel^2, hlr_blend in arcsec
    """

    if wcs is None:
        cen_ra = 0.5 * (mu[0].max() + mu[0].min()) * galsim.degrees
        cen_dec = 0.5 * (mu[1].max() + mu[1].min()) * galsim.degrees
        cen_coord = galsim.CelestialCoord(cen_ra, cen_dec)  #, gsparams=gsp)
        affine_wcs = galsim.PixelScale(pixel_scale).affine().withOrigin(
            galsim.PositionI(0, 0))
        wcs = galsim.TanWCS(affine_wcs,
                            world_origin=cen_coord)  #, gsparams=gsp)

    mu = galsim_world2pix(wcs, mu[0], mu[1])
    c = galsim_world2pix(wcs, [c[0]], [c[1]])  # assumes scalar c[0], c[1]

    Sigma = get_shape_covmat_fast(
        hlr / pixel_scale, e1, e2, hsm=hsm
    )  # an array filled with second moments tensors of the blend members
    A = flux / (2 * np.pi * np.linalg.det(Sigma)**0.5)

    # compute the second moments of the blend "system" (collectively)
    Q_blend = get_blend_moments(A, mu, c, Sigma, unit='pixel')
    hlr_blend, e1_blend, e2_blend = hlr_from_moments_fast(Q_blend,
                                                          hsm=hsm,
                                                          return_shape=True)

    to_return = [e1_blend, e2_blend]

    if out_unit.startswith('deg'):
        convertor = pixel_scale * 3600
    elif out_unit.startswith('arcmin'):
        convertor = pixel_scale * 60
    elif out_unit.startswith('arcsec'):
        convertor = pixel_scale
    elif out_unit.startswith('pix'):
        convertor = 1.0
    else:
        raise RuntimeError('Invalid `out_unit`')

    if return_hlr:
        to_return += [hlr_blend * convertor]

    if return_moments:
        to_return += [Q_blend * convertor**2]

    return to_return
Beispiel #26
0
    def __init__(self,
                 image,
                 x_interpolant=None,
                 k_interpolant=None,
                 normalization='flux',
                 scale=None,
                 wcs=None,
                 flux=None,
                 pad_factor=4.,
                 noise_pad_size=0,
                 noise_pad=0.,
                 rng=None,
                 pad_image=None,
                 calculate_stepk=True,
                 calculate_maxk=True,
                 use_cache=True,
                 use_true_center=True,
                 offset=None,
                 gsparams=None,
                 dx=None,
                 _force_stepk=0.,
                 _force_maxk=0.,
                 _serialize_stepk=None,
                 _serialize_maxk=None,
                 hdu=None):

        # Check for obsolete dx parameter
        if dx is not None and scale is None:
            from galsim.deprecated import depr
            depr('dx', 1.1, 'scale')
            scale = dx

        # If the "image" is not actually an image, try to read the image as a file.
        if not isinstance(image, galsim.Image):
            image = galsim.fits.read(image, hdu=hdu)

        # make sure image is really an image and has a float type
        if image.dtype != np.float32 and image.dtype != np.float64:
            raise ValueError(
                "Supplied image does not have dtype of float32 or float64!")

        # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor
        if not image.bounds.isDefined():
            raise ValueError("Supplied image does not have bounds defined!")

        # check what normalization was specified for the image: is it an image of surface
        # brightness, or flux?
        if not normalization.lower() in ("flux", "f", "surface brightness",
                                         "sb"):
            raise ValueError((
                "Invalid normalization requested: '%s'. Expecting one of 'flux', "
                + "'f', 'surface brightness', or 'sb'.") % normalization)

        # set up the interpolants if none was provided by user, or check that the user-provided ones
        # are of a valid type
        if x_interpolant is None:
            self.x_interpolant = galsim.Quintic(tol=1e-4)
        else:
            self.x_interpolant = galsim.utilities.convert_interpolant(
                x_interpolant)
        if k_interpolant is None:
            self.k_interpolant = galsim.Quintic(tol=1e-4)
        else:
            self.k_interpolant = galsim.utilities.convert_interpolant(
                k_interpolant)

        # Store the image as an attribute and make sure we don't change the original image
        # in anything we do here.  (e.g. set scale, etc.)
        self.image = image.view()
        self.use_cache = use_cache

        # Set the wcs if necessary
        if scale is not None:
            if wcs is not None:
                raise TypeError(
                    "Cannot provide both scale and wcs to InterpolatedImage")
            self.image.wcs = galsim.PixelScale(scale)
        elif wcs is not None:
            if not isinstance(wcs, galsim.BaseWCS):
                raise TypeError(
                    "wcs parameter is not a galsim.BaseWCS instance")
            self.image.wcs = wcs
        elif self.image.wcs is None:
            raise ValueError(
                "No information given with Image or keywords about pixel scale!"
            )

        # Set up the GaussianDeviate if not provided one, or check that the user-provided one is
        # of a valid type.
        if rng is None:
            if noise_pad: rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError(
                "rng provided to InterpolatedImage constructor is not a BaseDeviate"
            )

        # Check that given pad_image is valid:
        if pad_image:
            if isinstance(pad_image, str):
                pad_image = galsim.fits.read(pad_image)
            if not isinstance(pad_image, galsim.Image):
                raise ValueError("Supplied pad_image is not an Image!")
            if pad_image.dtype != np.float32 and pad_image.dtype != np.float64:
                raise ValueError(
                    "Supplied pad_image is not one of the allowed types!")

        # Check that the given noise_pad is valid:
        try:
            noise_pad = float(noise_pad)
        except:
            pass
        if isinstance(noise_pad, float):
            if noise_pad < 0.:
                raise ValueError("Noise variance cannot be negative!")
        # There are other options for noise_pad, the validity of which will be checked in
        # the helper function self.buildNoisePadImage()

        # This will be passed to SBInterpolatedImage, so make sure it is the right type.
        pad_factor = float(pad_factor)
        if pad_factor <= 0.:
            raise ValueError("Invalid pad_factor <= 0 in InterpolatedImage")

        if use_true_center:
            im_cen = self.image.bounds.trueCenter()
        else:
            im_cen = self.image.bounds.center()

        local_wcs = self.image.wcs.local(image_pos=im_cen)
        self.min_scale = local_wcs.minLinearScale()
        self.max_scale = local_wcs.maxLinearScale()

        # Make sure the image fits in the noise pad image:
        if noise_pad_size:
            import math
            # Convert from arcsec to pixels according to the local wcs.
            # Use the minimum scale, since we want to make sure noise_pad_size is
            # as large as we need in any direction.
            noise_pad_size = int(math.ceil(noise_pad_size / self.min_scale))
            # Round up to a good size for doing FFTs
            noise_pad_size = galsim._galsim.goodFFTSize(noise_pad_size)
            if noise_pad_size <= min(self.image.array.shape):
                # Don't need any noise padding in this case.
                noise_pad_size = 0
            elif noise_pad_size < max(self.image.array.shape):
                noise_pad_size = max(self.image.array.shape)

        # See if we need to pad out the image with either a pad_image or noise_pad
        if noise_pad_size:
            new_pad_image = self.buildNoisePadImage(noise_pad_size, noise_pad,
                                                    rng)

            if pad_image:
                # if both noise_pad and pad_image are set, then we need to build up a larger
                # pad_image and place the given pad_image in the center.

                # We will change the bounds here, so make a new view to avoid modifying the
                # input pad_image.
                pad_image = pad_image.view()
                pad_image.setCenter(0, 0)
                new_pad_image.setCenter(0, 0)
                if new_pad_image.bounds.includes(pad_image.bounds):
                    new_pad_image[pad_image.bounds] = pad_image
                else:
                    new_pad_image = pad_image

            pad_image = new_pad_image

        elif pad_image:
            # Just make sure pad_image is the right type
            pad_image = galsim.Image(pad_image, dtype=image.dtype)

        # Now place the given image in the center of the padding image:
        if pad_image:
            pad_image.setCenter(0, 0)
            self.image.setCenter(0, 0)
            if pad_image.bounds.includes(self.image.bounds):
                pad_image[self.image.bounds] = self.image
                pad_image.wcs = self.image.wcs
            else:
                # If padding was smaller than original image, just use the original image.
                pad_image = self.image
        else:
            pad_image = self.image

        # GalSim cannot automatically know what stepK and maxK are appropriate for the
        # input image.  So it is usually worth it to do a manual calculation (below).
        #
        # However, there is also a hidden option to force it to use specific values of stepK and
        # maxK (caveat user!).  The values of _force_stepk and _force_maxk should be provided in
        # terms of physical scale, e.g., for images that have a scale length of 0.1 arcsec, the
        # stepK and maxK should be provided in units of 1/arcsec.  Then we convert to the 1/pixel
        # units required by the C++ layer below.  Also note that profile recentering for even-sized
        # images (see the ._fix_center step below) leads to automatic reduction of stepK slightly
        # below what is provided here, while maxK is preserved.
        if _force_stepk > 0.:
            calculate_stepk = False
            _force_stepk *= self.min_scale
        if _force_maxk > 0.:
            calculate_maxk = False
            _force_maxk *= self.max_scale

        # Due to floating point rounding errors, for pickling it's necessary to store the exact
        # _force_maxk and _force_stepk used to create the SBInterpolatedImage, as opposed to the
        # values before being scaled by self.min_scale and self.max_scale.  So we do that via the
        # _serialize_maxk and _serialize_stepk hidden kwargs, which should only get used during
        # pickling.
        if _serialize_stepk is not None:
            calculate_stepk = False
            _force_stepk = _serialize_stepk
        if _serialize_maxk is not None:
            calculate_maxk = False
            _force_maxk = _serialize_maxk

        # Save these values for pickling
        self._pad_image = pad_image
        self._pad_factor = pad_factor
        self._gsparams = gsparams

        # Make the SBInterpolatedImage out of the image.
        sbii = galsim._galsim.SBInterpolatedImage(pad_image.image,
                                                  self.x_interpolant,
                                                  self.k_interpolant,
                                                  pad_factor, _force_stepk,
                                                  _force_maxk, gsparams)

        # I think the only things that will mess up if getFlux() == 0 are the
        # calculateStepK and calculateMaxK functions, and rescaling the flux to some value.
        if (calculate_stepk or calculate_maxk
                or flux is not None) and sbii.getFlux() == 0.:
            raise RuntimeError(
                "This input image has zero total flux. "
                "It does not define a valid surface brightness profile.")

        if calculate_stepk:
            if calculate_stepk is True:
                sbii.calculateStepK()
            else:
                # If not a bool, then value is max_stepk
                sbii.calculateStepK(max_stepk=calculate_stepk)
        if calculate_maxk:
            if calculate_maxk is True:
                sbii.calculateMaxK()
            else:
                # If not a bool, then value is max_maxk
                sbii.calculateMaxK(max_maxk=calculate_maxk)

        # If the user specified a surface brightness normalization for the input Image, then
        # need to rescale flux by the pixel area to get proper normalization.
        if flux is None and normalization.lower() in [
                'surface brightness', 'sb'
        ]:
            flux = sbii.getFlux() * local_wcs.pixelArea()

        # Save this intermediate profile
        self._sbii = sbii
        self._stepk = sbii.stepK() / self.min_scale
        self._maxk = sbii.maxK() / self.max_scale
        self._flux = flux

        self._serialize_stepk = sbii.stepK()
        self._serialize_maxk = sbii.maxK()

        prof = GSObject(sbii)

        # Make sure offset is a PositionD
        offset = prof._parse_offset(offset)

        # Apply the offset, and possibly fix the centering for even-sized images
        # Note reverse=True, since we want to fix the center in the opposite sense of what the
        # draw function does.
        prof = prof._fix_center(self.image.array.shape,
                                offset,
                                use_true_center,
                                reverse=True)

        # Save the offset we will need when pickling.
        if hasattr(prof, 'offset'):
            self._offset = -prof.offset
        else:
            self._offset = None

        # Bring the profile from image coordinates into world coordinates
        prof = local_wcs.toWorld(prof)

        # If the user specified a flux, then set to that flux value.
        if flux is not None:
            prof = prof.withFlux(float(flux))

        # Now, in order for these to pickle correctly if they are the "original" object in a
        # Transform object, we need to hide the current transformation.  An easy way to do that
        # is to hide the SBProfile in an SBAdd object.
        sbp = galsim._galsim.SBAdd([prof.SBProfile])

        GSObject.__init__(self, sbp)
    def __init__(
            self, *,
            frac_ex=0, rng, sel_rng,
            gal_type, psf_type, scale,
            shear_scene=True,
            n_coadd=1,
            n_coadd_psf=None,
            n_coadd_msk=1,
            g1=0.02, g2=0.0,
            g1ex=0.02, g2ex=0.0,
            dim=225, buff=25,
            noise=180,
            ngal=45.0,
            n_bands=1,
            gal_grid=None,
            psf_kws=None,
            gal_kws=None,
            homogenize_psf=False,
            mask_and_interp=False,
            add_bad_columns=True,
            add_cosmic_rays=True,
            bad_columns_kws=None,
            interpolation_type='cubic',
            ngal_factor=None):
        self.rng = rng
        self.sel_rng = sel_rng
        self.frac_ex = frac_ex
        if self.frac_ex == 0:
            self.flag_ex = False
        else:
            self.flag_ex = True
        self.noise_rng = np.random.RandomState(seed=rng.randint(1, 2**32-1))
        self.gal_type = gal_type
        self.psf_type = psf_type
        self.n_coadd = n_coadd
        self.n_coadd_msk = n_coadd_msk
        self.g1 = g1
        self.g2 = g2
        self.g1ex = g1ex
        self.g2ex = g2ex
        self.shear_scene = shear_scene
        self.dim = dim
        self.buff = buff
        self.ngal_factor = ngal_factor
        # assumed to be one band unless otherwise specified via the
        # galaxy type
        self.noise = [noise / np.sqrt(self.n_coadd)] * n_bands
        self.ngal = ngal
        self.gal_grid = gal_grid
        self.im_cen = (dim - 1) / 2
        self.psf_kws = psf_kws
        self.gal_kws = gal_kws
        self.n_coadd_psf = n_coadd_psf or self.n_coadd
        self.homogenize_psf = homogenize_psf
        self.mask_and_interp = mask_and_interp
        self.add_bad_columns = add_bad_columns
        self.add_cosmic_rays = add_cosmic_rays
        self.bad_columns_kws = bad_columns_kws or {}
        self.interpolation_type = interpolation_type

        self.area_sqr_arcmin = ((self.dim - 2*self.buff) * scale / 60)**2

        self._galsim_rng = galsim.BaseDeviate(
            seed=self.rng.randint(low=1, high=2**32-1))

        # typical pixel scale
        self.scale = scale
        self.wcs = galsim.PixelScale(self.scale)

        # frac of a single dimension that is used for drawing objects
        frac = 1.0 - self.buff * 2 / self.dim

        # half of the width of center of the patch that has objects
        self.pos_width = self.dim * frac * 0.5 * self.scale

        # for wldeblend galaxies, we have to adjust some of the input
        # parameters since they are computed self consisently from the
        # input catalog and/or package defaults
        if self.gal_type == 'wldeblend':
            self._extra_init_for_wldeblend()

        # given the input number of objects to simulate per square arcminute,
        # compute the number we actually need
        if self.ngal_factor is None:
            self.ngal_factor = 1
        LOGGER.info('ngal adjustment factor: %f', self.ngal_factor)

        self.nobj = int(
            self.ngal * self.ngal_factor *
            (self.dim * self.scale / 60 * frac)**2)

        self.shear_mat = galsim.Shear(g1=self.g1, g2=self.g2).getMatrix()
        self.shear_matex = galsim.Shear(g1=self.g1ex, g2=self.g2ex).getMatrix()

        if self.gal_grid is not None:
            self.nobj = self.gal_grid * self.gal_grid

        self.n_bands = len(self.noise)

        LOGGER.info('simulating %d bands', self.n_bands)
def distributed_strategy(args):
    print(
        f"Started worker {THIS_WORKER} at {datetime.now().strftime('%y-%m-%d_%H-%M-%S')}"
    )
    options = tf.io.TFRecordOptions(compression_type=args.compression_type)

    catalog = galsim.COSMOSCatalog(sample=args.sample,
                                   dir=args.cosmos_dir,
                                   exclusion_level=args.exclusion_level,
                                   min_flux=args.min_flux)
    n_galaxies = catalog.getNObjects()
    cat_param = catalog.param_cat[catalog.orig_index]
    sparams = cat_param['sersicfit']
    cat_param = append_fields(cat_param, 'sersic_q', sparams[:, 3])
    cat_param = append_fields(cat_param, 'sersic_n', sparams[:, 2])

    with tf.io.TFRecordWriter(
            os.path.join(args.output_dir, f"data_{THIS_WORKER}.tfrecords"),
            options) as writer:
        for index in range((THIS_WORKER - 1), n_galaxies, N_WORKERS):
            gal = catalog.makeGalaxy(index,
                                     noise_pad_size=args.pixels *
                                     args.pixel_scale)
            psf = gal.original_psf

            # We save the corresponding attributes for this galaxy
            if hasattr(args, 'attributes'):
                params = cat_param[index]
                attributes = {k: params[k] for k in args.attributes}
            else:
                attributes = None

            # Apply the PSF
            gal = galsim.Convolve(gal, psf)

            # Compute sqrt of absolute noise power spectrum, at the resolution and stamp size of target image
            ps = gal.noise._get_update_rootps(
                (args.pixels, args.pixels),
                wcs=galsim.PixelScale(args.pixel_scale))

            # We draw the pixel image of the convolved image
            im = gal.drawImage(nx=args.pixels,
                               ny=args.pixels,
                               scale=args.pixel_scale,
                               method='no_pixel',
                               use_true_center=False).array.astype('float32')

            # preprocess image
            # For correlated noise, we estimate that the sqrt of the Energy Spectral Density of the noise at (f_x=f_y=0)
            # is a good estimate of the STD
            if args.preprocess:
                im = tf.nn.relu(im - ps[0, 0]).numpy(
                )  # subtract background, fold negative pixels to 0
                im /= im.max()  # normalize peak to 1
                signal_pixels = np.sum(
                    im > args.signal_threshold
                )  # how many pixels have a value above a certain threshold
                if signal_pixels < args.signal_pixels:  # argument used to select only examples that are more distinct galaxy features (it does however bias the dataset in redshift space)
                    continue

            # Draw a kimage of the galaxy, just to figure out what maxk is, there might
            # be more efficient ways to do this though...
            bounds = galsim.BoundsI(0, args.pixels // 2, -args.pixels // 2,
                                    args.pixels // 2 - 1)
            imG = gal.drawKImage(bounds=bounds,
                                 scale=2. * np.pi /
                                 (args.pixels * args.pixels),
                                 recenter=False)
            mask = ~(np.fft.fftshift(imG.array, axes=0) == 0)

            # Draw the Fourier domain image of the galaxy, using x1 zero padding,
            # and x2 subsampling
            interp_factor = 2
            padding_factor = 1
            Nk = args.pixels * interp_factor * padding_factor
            bounds = galsim.BoundsI(0, Nk // 2, -Nk // 2, Nk // 2 - 1)
            imCp = psf.drawKImage(bounds=bounds,
                                  scale=2. * np.pi /
                                  (Nk * args.pixel_scale / interp_factor),
                                  recenter=False)

            # Transform the psf array into proper format, remove the phase
            im_psf = np.abs(np.fft.fftshift(imCp.array,
                                            axes=0)).astype('float32')

            # The following comes from correlatednoise.py
            rt2 = np.sqrt(2.)
            shape = (args.pixels, args.pixels)
            ps[0, 0] = rt2 * ps[0, 0]
            # Then make the changes necessary for even sized arrays
            if shape[1] % 2 == 0:  # x dimension even
                ps[0, shape[1] // 2] = rt2 * ps[0, shape[1] // 2]
            if shape[0] % 2 == 0:  # y dimension even
                ps[shape[0] // 2, 0] = rt2 * ps[shape[0] // 2, 0]
                # Both dimensions even
                if shape[1] % 2 == 0:
                    ps[shape[0] // 2,
                       shape[1] // 2] = rt2 * ps[shape[0] // 2, shape[1] // 2]

            # Apply mask to power spectrum so that it is very large outside maxk
            ps = np.where(mask, np.log(ps**2), 10).astype('float32')
            features = {
                "image": _bytes_feature(im.tobytes()),
                "height": _int64_feature(im.shape[0]),
                "width": _int64_feature(im.shape[1]),
                "psf": _bytes_feature(im_psf.tobytes()),
                "ps":
                _bytes_feature(ps.tobytes()),  # power spectrum of the noise
            }

            # Adding the parameters provided
            if attributes is not None:
                for k in attributes:
                    features['attrs_' + k] = _float_feature(attributes[k])

            record = tf.train.Example(features=tf.train.Features(
                feature=features)).SerializeToString()
            writer.write(record)
    print(
        f"Finished worker {THIS_WORKER} at {datetime.now().strftime('%y-%m-%d_%H-%M-%S')}"
    )
def get_exp_list_coadd(m, i, m2=None):
    def make_jacobian(dudx, dudy, dvdx, dvdy, x, y):
        j = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy)
        return j.withOrigin(galsim.PositionD(x, y))

    oversample = 1
    #def psf_offset(i,j,star_):
    m3 = [0]
    #relative_offset=[0]
    for jj, psf_ in enumerate(m2):  # m2 has psfs for each observation.
        if jj == 0:
            continue
        gal_stamp_center_row = m['orig_start_row'][i][jj] + m['box_size'][
            i] / 2 - 0.5  # m['box_size'] is the galaxy stamp size.
        gal_stamp_center_col = m['orig_start_col'][i][jj] + m['box_size'][
            i] / 2 - 0.5  # m['orig_start_row/col'] is in SCA coordinates.
        psf_stamp_size = 32

        # Make the bounds for the psf stamp.
        b = galsim.BoundsI(
            xmin=(m['orig_start_col'][i][jj] +
                  (m['box_size'][i] - 32) / 2. - 1) * oversample + 1,
            xmax=(m['orig_start_col'][i][jj] +
                  (m['box_size'][i] - 32) / 2. + psf_stamp_size - 1) *
            oversample,
            ymin=(m['orig_start_row'][i][jj] +
                  (m['box_size'][i] - 32) / 2. - 1) * oversample + 1,
            ymax=(m['orig_start_row'][i][jj] +
                  (m['box_size'][i] - 32) / 2. + psf_stamp_size - 1) *
            oversample)

        # Make wcs for oversampled psf.
        wcs_ = make_jacobian(
            m.get_jacobian(i, jj)['dudcol'] / oversample,
            m.get_jacobian(i, jj)['dudrow'] / oversample,
            m.get_jacobian(i, jj)['dvdcol'] / oversample,
            m.get_jacobian(i, jj)['dvdrow'] / oversample,
            m['orig_col'][i][jj] * oversample,
            m['orig_row'][i][jj] * oversample)
        # Taken from galsim/roman_psfs.py line 266. Update each psf to an object-specific psf using the wcs.
        scale = galsim.PixelScale(wfirst.pixel_scale / oversample)
        psf_ = wcs_.toWorld(scale.toImage(psf_),
                            image_pos=galsim.PositionD(wfirst.n_pix / 2,
                                                       wfirst.n_pix / 2))

        # Convolve with the star model and get the psf stamp.
        #st_model = galsim.DeltaFunction(flux=1.)
        #st_model = st_model.evaluateAtWavelength(wfirst.getBandpasses(AB_zeropoint=True)['H158'].effective_wavelength)
        #st_model = st_model.withFlux(1.)
        #st_model = galsim.Convolve(st_model, psf_)
        psf_ = galsim.Convolve(psf_, galsim.Pixel(wfirst.pixel_scale))
        psf_stamp = galsim.Image(b, wcs=wcs_)

        # Galaxy is being drawn with some subpixel offsets, so we apply the offsets when drawing the psf too.
        offset_x = m['orig_col'][i][jj] - gal_stamp_center_col
        offset_y = m['orig_row'][i][jj] - gal_stamp_center_row
        offset = galsim.PositionD(offset_x, offset_y)
        if (offset_x <= -1.0 or offset_y <= -1.0):
            print(offset)
        elif (offset_x >= 1.0 or offset_y >= 1.0):
            print(offset)
        psf_.drawImage(image=psf_stamp, offset=offset, method='no_pixel')
        m3.append(psf_stamp.array)

    obs_list = ObsList()
    psf_list = ObsList()

    included = []
    w = []
    # For each of these objects create an observation
    for j in range(m['ncutout'][i]):
        if j == 0:
            continue
        # if j>1:
        #     continue
        im = m.get_cutout(i, j, type='image')
        weight = m.get_cutout(i, j, type='weight')

        im_psf = m3[j]
        im_psf2 = im_psf
        if np.sum(im) == 0.:
            #print(local_meds, i, j, np.sum(im))
            print('no flux in image ', i, j)
            continue

        jacob = m.get_jacobian(i, j)
        # Get a galaxy jacobian.
        gal_jacob = Jacobian(
            row=(m['orig_row'][i][j] - m['orig_start_row'][i][j]),
            col=(m['orig_col'][i][j] - m['orig_start_col'][i][j]),
            dvdrow=jacob['dvdrow'],
            dvdcol=jacob['dvdcol'],
            dudrow=jacob['dudrow'],
            dudcol=jacob['dudcol'])

        psf_center = (32 / 2.) + 0.5
        # Get a oversampled psf jacobian.
        if oversample == 1:
            psf_jacob2 = Jacobian(
                row=15.5 +
                (m['orig_row'][i][j] - m['orig_start_row'][i][j] + 1 -
                 (m['box_size'][i] / 2. + 0.5)) * oversample,
                col=15.5 +
                (m['orig_col'][i][j] - m['orig_start_col'][i][j] + 1 -
                 (m['box_size'][i] / 2. + 0.5)) * oversample,
                dvdrow=jacob['dvdrow'] / oversample,
                dvdcol=jacob['dvdcol'] / oversample,
                dudrow=jacob['dudrow'] / oversample,
                dudcol=jacob['dudcol'] / oversample)
        elif oversample == 4:
            psf_jacob2 = Jacobian(
                row=63.5 +
                (m['orig_row'][i][j] - m['orig_start_row'][i][j] + 1 -
                 (m['box_size'][i] / 2. + 0.5)) * oversample,
                col=63.5 +
                (m['orig_col'][i][j] - m['orig_start_col'][i][j] + 1 -
                 (m['box_size'][i] / 2. + 0.5)) * oversample,
                dvdrow=jacob['dvdrow'] / oversample,
                dvdcol=jacob['dvdcol'] / oversample,
                dudrow=jacob['dudrow'] / oversample,
                dudcol=jacob['dudcol'] / oversample)

        # Create an obs for each cutout
        mask = np.where(weight != 0)
        if 1. * len(weight[mask]) / np.product(np.shape(weight)) < 0.8:
            continue

        w.append(np.mean(weight[mask]))
        noise = np.ones_like(weight) / w[-1]

        psf_obs = Observation(im_psf,
                              jacobian=gal_jacob,
                              meta={
                                  'offset_pixels': None,
                                  'file_id': None
                              })
        psf_obs2 = Observation(im_psf2,
                               jacobian=psf_jacob2,
                               meta={
                                   'offset_pixels': None,
                                   'file_id': None
                               })
        #obs = Observation(im, weight=weight, jacobian=gal_jacob, psf=psf_obs, meta={'offset_pixels':None,'file_id':None})
        # oversampled PSF
        obs = Observation(im,
                          weight=weight,
                          jacobian=gal_jacob,
                          psf=psf_obs2,
                          meta={
                              'offset_pixels': None,
                              'file_id': None
                          })
        obs.set_noise(noise)

        obs_list.append(obs)
        psf_list.append(psf_obs2)
        included.append(j)

    return obs_list, psf_list, np.array(included) - 1, np.array(w)
Beispiel #30
0
def BuildWCS(config, logger=None):
    """Read the wcs from the config dict, writing both it and, if it is well-defined, the 
    pixel_scale to the config.  If the wcs does not have a well-defined pixel_scale, it will 
    be stored as None.
    """
    image = config['image']

    # If there is a wcs field, read it and update the wcs variable.
    if 'wcs' in image:
        image_wcs = image['wcs']
        if 'type' in image_wcs:
            type = image_wcs['type']
        else:
            type = 'PixelScale'

        # Special case: origin == center means to use image_center for the wcs origin
        if 'origin' in image_wcs and image_wcs['origin'] == 'center':
            origin = config['image_center']
            if logger:
                logger.debug('image %d: Using origin = %s',
                             config['image_num'], str(origin))
            image_wcs['origin'] = origin

        if type not in valid_wcs_types:
            raise AttributeError("Invalid image.wcs.type=%s." % type)

        if 'origin' in image_wcs or 'world_origin' in image_wcs:
            build_func = eval(valid_wcs_types[type][1])
        else:
            build_func = eval(valid_wcs_types[type][0])

        if logger:
            logger.debug('image %d: Build WCS for type = %s using %s',
                         config['image_num'], type, str(build_func))

        req = build_func._req_params
        opt = build_func._opt_params
        single = build_func._single_params

        # Pull in the image layer pixel_scale as a scale item if necessary.
        if (('scale' in req or 'scale' in opt) and 'scale' not in image_wcs
                and 'pixel_scale' in image):
            image_wcs['scale'] = image['pixel_scale']

        kwargs, safe = galsim.config.GetAllParams(image_wcs, type, config, req,
                                                  opt, single)

        if logger and build_func._takes_logger:
            kwargs['logger'] = logger

        # This would be weird, but might as well check...
        if build_func._takes_rng:
            if 'rng' not in config:
                raise ValueError(
                    "No config['rng'] available for %s.type = %s" %
                    (key, type))
            kwargs['rng'] = config['rng']

        if logger:
            logger.debug('image %d: kwargs = %s', config['image_num'],
                         str(kwargs))
        wcs = build_func(**kwargs)

    else:
        # Default if no wcs is to use PixelScale
        if 'pixel_scale' in image:
            scale = galsim.config.ParseValue(image, 'pixel_scale', config,
                                             float)[0]
        else:
            scale = 1.0
        wcs = galsim.PixelScale(scale)

    # Write it to the config dict and also return it.
    config['wcs'] = wcs

    # If the WCS is a PixelScale or OffsetWCS, then store the pixel_scale in base.  The
    # config apparatus does not use it -- we always use the wcs -- but we keep it in case
    # the user wants to use it for an Eval item.  It's one of the variables they are allowed
    # to assume will be present for them.
    if wcs.isPixelScale():
        config['pixel_scale'] = wcs.scale

    return wcs