Example #1
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
Example #2
0
 def __setstate__(self, d):
     self.__dict__ = d
     psf_inv = galsim.Deconvolve(self.original_psf, gsparams=self._gsparams)
     GSObject.__init__(
         self,
         galsim.Convolve([self.original_gal, psf_inv],
                         gsparams=self._gsparams))
Example #3
0
def gs_Deconvolve(psf_img, pixel_scale=0.2, interp_factor=2, padding_factor=1):
    """
  Returns a deconvolution kernel of a psf image.
  
  Args:
      psf_img: numpy array representing the psf model
      pixel_scale: the pixel scale of the image, in arcsec/pixel
      interp_factor: the interpolation factor for super-resolution
      padding_factor: a factor to add side pads to the image
  Returns:
      A complex tensorflow tensor that is a deconvolution kernel.
  
  """

    N = len(psf_img)

    psf_galsim = galsim.InterpolatedImage(
        galsim.Image(psf_img, scale=pixel_scale))
    ipsf = galsim.Deconvolve(psf_galsim)
    Nk = N * interp_factor * padding_factor
    bounds = galsim._BoundsI(-Nk // 2, Nk // 2 - 1, -Nk // 2, Nk // 2 - 1)
    imipsf = ipsf.drawKImage(bounds=bounds,
                             scale=2. * np.pi /
                             (N * padding_factor * pixel_scale),
                             recenter=False)
    return tf.convert_to_tensor(imipsf.array, dtype=tf.complex64)
Example #4
0
def test_wcs():
    """Reproduce an error Erin found and reported in #834.  This was never an error in a released
    version, just temporarily on master, but the mistake hadn't been caught by any of our unit
    tests, so this test catches the error.
    """
    wcs = galsim.JacobianWCS(0.01, -0.26, -0.28, -0.03)
    gal = galsim.Exponential(half_light_radius=1.1, flux=237)
    psf = galsim.Moffat(beta=3.5, half_light_radius=0.9)
    obs = galsim.Convolve(gal, psf)

    obs_im = obs.drawImage(nx=32, ny=32, offset=(0.3, -0.2), wcs=wcs)
    psf_im = psf.drawImage(nx=32, ny=32, wcs=wcs)

    ii = galsim.InterpolatedImage(obs_im)
    psf_ii = galsim.InterpolatedImage(psf_im)
    psf_inv = galsim.Deconvolve(psf_ii)

    ii_nopsf = galsim.Convolve(ii, psf_inv)

    newpsf = galsim.Moffat(beta=3.5, half_light_radius=0.95)
    newpsf = newpsf.dilate(1.02)

    new_ii = ii_nopsf.shear(g1=0.01, g2=0.0)
    new_ii = galsim.Convolve(new_ii, newpsf)

    new_im = new_ii.drawImage(image=obs_im.copy(), method='no_pixel')
    np.testing.assert_almost_equal(new_im.array.sum() / 237,
                                   obs_im.array.sum() / 237,
                                   decimal=1)
Example #5
0
    def _set_data(self):
        """
        create galsim objects based on the input observation
        """

        obs = self.obs

        # these would share data with the original numpy arrays, make copies
        # to be sure they don't get modified
        #
        self.image = galsim.Image(obs.image.copy(), wcs=self.get_wcs())

        self.psf_image = galsim.Image(obs.psf.image.copy(),
                                      wcs=self.get_psf_wcs())

        # interpolated psf image
        psf_int = galsim.InterpolatedImage(self.psf_image,
                                           x_interpolant=self.interp)

        # this can be used to deconvolve the psf from the galaxy image
        psf_int_inv = galsim.Deconvolve(psf_int)

        self.image_int = galsim.InterpolatedImage(self.image,
                                                  x_interpolant=self.interp)

        # deconvolved galaxy image, psf+pixel removed
        self.image_int_nopsf = galsim.Convolve(self.image_int, psf_int_inv)

        # interpolated psf deconvolved from pixel.  This is what
        # we dilate, shear, etc and reconvolve the image by
        if self.symmetrize_psf:
            self.psf_int_nopix = self._get_symmetrized_psf_nopix()
        else:
            self.psf_int_nopix = galsim.Convolve([psf_int, self.pixel_inv])
Example #6
0
def decon_with_PSF(image, psf):
    """
    image is a Galsim.image object
    psf is a Galsim.interpolatedImage object
    """
    HST_inv_psf = galsim.Deconvolve(psf)
    deconv_HST_image = galsim.Convolve(HST_inv_psf, image)

    return deconv_HST_image
Example #7
0
    def _set_pixel(self):
        """
        set the pixel based on the pixel scale, for convolutions

        Thanks to M. Jarvis for the suggestion to use toWorld
        to get the proper pixel
        """

        wcs = self.get_wcs()
        self.pixel = wcs.toWorld(galsim.Pixel(scale=1))
        self.pixel_inv = galsim.Deconvolve(self.pixel)
Example #8
0
def main(argv):

    # translation from C++ by Jim; comments after this one are in Gary's voice

    l3 = galsim.Lanczos(3, True, 1.0E-4)
    l32d = galsim.InterpolantXY(l3)

    dxHST = 0.03
    dxSDSS = 0.396
    g1 = 0.02
    g2 = 0.0
    psfSky = 1000.0

    rootname = argv[1]
    xshift = float(argv[2]) if len(argv) > 2 else 0.
    yshift = float(argv[3]) if len(argv) > 3 else 0.
    s = galsim.Shear(g1=g1, g2=g2)

    # Rachel is probably using the (1+g, 1-g) form of shear matrix,
    # which means there is some (de)magnification, by my definition:
    #e = galsim.Ellipse(s, -(g1*g1+g2*g2), galsim.PositionD(xshift,yshift));

    galaxyImg = galsim.fits.read(rootname + "_masknoise.fits")
    galaxy = galsim.InterpolatedImage(galaxyImg,
                                      x_interpolant=l32d,
                                      dx=dxHST,
                                      flux=0.804 * 1000. * dxSDSS * dxSDSS)

    psf1Img = galsim.fits.read(rootname + ".psf.fits")
    psf1 = galsim.InterpolatedImage(psf1Img,
                                    x_interpolant=l32d,
                                    dx=dxHST,
                                    flux=1.)

    psf2Img = galsim.fits.read(rootname + ".sdsspsf.fits")
    psf2 = galsim.InterpolatedImage(psf2Img,
                                    x_interpolant=l32d,
                                    dx=dxSDSS,
                                    flux=1.)

    outImg = galsim.fits.read(rootname + ".g1_0.02.g2_0.00.fits")
    result = outImg.copy()

    psfInv = galsim.Deconvolve(psf1)
    deconv = galsim.Convolve(galaxy, psfInv)
    sheared = deconv.createTransformed(e)
    out = galsim.Convolve(sheared, psf2)

    test_outImg = out.draw(result, dx=dxSDSS)
    test_outImg.write(rootname + ".gary.fits")
    result += psfSky
    result -= test_outImg
    result.write(rootname + ".diff.fits")
Example #9
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
Example #10
0
def get_gal_nocg(Args, gal_cg, chr_PSF):
    """ Construct a galaxy SBP with no CG that yields the same PSF convolved
    image as the given galaxy with CG convolved with the PSF.

    To reduduce pixelization effects, resolution is incresed 4 times when
    drawing images of effective PSF and PSF convolved galaxy with CG. These
    images don't represent physical objects that the telescope will see.

    @param Args    Class with the following attributes:
        Args.npix   Number of pixels across square postage stamp image.
        Args.scale  Pixel scale for postage stamp image.
        Args.bp     GalSim Bandpass describing filter.
        Args.c_SED  Flux weighted composite SED.
    @param gal_cg   GalSim GSObject describing SBP of galaxy with CG.
    @param chr_PSF  GalSim ChromaticObject describing the chromatic PSF.
    @return     SBP of galaxy with no CG, with composite SED.
    """
    # PSF is convolved with a delta function to draw effective psf image
    star = galsim.Gaussian(half_light_radius=1e-9) * Args.c_SED
    con = galsim.Convolve(chr_PSF, star)
    psf_eff_img = con.drawImage(Args.bp,
                                scale=Args.scale / 4.0,
                                ny=Args.npix * 4.0,
                                nx=Args.npix * 4.0,
                                method='no_pixel')
    psf_eff = galsim.InterpolatedImage(psf_eff_img,
                                       calculate_stepk=False,
                                       calculate_maxk=False)
    con = galsim.Convolve(gal_cg, chr_PSF)
    gal_cg_eff_img = con.drawImage(Args.bp,
                                   scale=Args.scale / 4.0,
                                   nx=Args.npix * 6,
                                   ny=Args.npix * 6,
                                   method='no_pixel')
    # print "Get effective galaxy "
    gal_cg_eff = galsim.InterpolatedImage(gal_cg_eff_img,
                                          calculate_stepk=False,
                                          calculate_maxk=False)
    gal_nocg = galsim.Convolve(gal_cg_eff, galsim.Deconvolve(psf_eff))
    return gal_nocg * Args.c_SED
Example #11
0
def measureShapeReconv(gal, epsf, psf_galsample, galscale, redrawScaleFactor, shear_est, noise, noiseSNR,
                       return_type, artificialShear, psfii,
                       interpolant='lanczos100', gsparams=gsparams_default):
    """
    Convolve galaxy with epsf, draw image, interpolate, deconvolve by psfii, shear, reconvolve by dilated psf, measure shape.
    Parameters:
    gal:   galsim object representing the galaxy profile
    epsf:  galsim object representing the effective psf profile (w/ pixel response)
    psf_galsample: galsim Image of the **DILATED** epsf that you will reconvolve by, *sampled at galaxy rate* (ss)
    galscale: float, sampling scale of the galaxy (pixel_scale/gal_oversample)
    shear_est: string, the shear estimator to use in shape measurement. Can be "REGAUSS" or "KSB".
    noise: Galsim noise object or None; If not None, the generator of noise to be added to each image.
    noiseSNR: float or None; If not None, the SNR of the galaxy after adding noise at constant flux.
    return_type: string, can be "image", "observed", or "corrected"
    artificialShear: galsim Shear object, the artificial shear to be applied to the galaxy.
    psfii: interpolated image of the oversampled psf
    interpolant: string, default 'lanczos100'
    gsparams: Galsim gsparams, default gsparams_default (given at beginning of this file).

    Convolve gal+epsf profiles, create interpolated image, de/reconvolve by psfii, measure shape
    Returns either the image or the observed or corrected (e1, e2) or (g1, g2) of reconvolved image
    See measureShapeBasic for full description of return signature
    """
    fin = galsim.Convolve(gal, epsf)
    given_im = fin.drawImage(scale=galscale, method='no_pixel')
    if noise is not None:
        if noiseSNR is not None:
            given_im.addNoiseSNR(noise, snr=noiseSNR, preserve_flux=True)
        else:
            given_im.addNoise(noise)
    gal_interp = galsim.InterpolatedImage(given_im, gsparams=gsparams, x_interpolant=interpolant)
    inv_psf = galsim.Deconvolve(psfii)
    dec = galsim.Convolve(gal_interp, inv_psf)
    if interpolant == "sinc" and artificialShear == galsim.Shear(g1=0, g2=0):
        # Breaks if sinc, artificialShear=0, and try to reconvolve with analytic psf
        raise Exception("Due to possible bug, cannot use sinc/zero shear/non-interpolated psf to reconvolve")
    dil_psf = dilatePSF(epsf, artificialShear)  # CHANGE
    shear_gal = dec.shear(g1=artificialShear.g1, g2=artificialShear.g2)
    return measureShapeBasic(shear_gal, dil_psf, psf_galsample, galscale, redrawScaleFactor, shear_est, None, None, return_type)
Example #12
0
    def _set_data(self, gal_image, psf_image_orig):

        psf_image = psf_image_orig.copy()

        imsum=psf_image.array.sum()
        if imsum == 0.0:
            raise DeconvRangeError("PSF image has zero flux")

        psf_image *= (1.0/imsum)

        self.igal = galsim.InterpolatedImage(
            gal_image,
            x_interpolant=XINTERP,
        )
        self.ipsf = galsim.InterpolatedImage(
            psf_image,
            x_interpolant=XINTERP,
        )

        # make dimensions odd
        wmult=1.0
        self.dim = 1 + self.ipsf.SBProfile.getGoodImageSize(
            self.ipsf.nyquistScale(),
            wmult,
        )
        self.dk=self.ipsf.stepK()

        self.ipsf_inv = galsim.Deconvolve(self.ipsf)

        self.igal_nopsf = galsim.Convolve(self.igal, self.ipsf_inv)

        self.psf_kreal,self.psf_kimag=self.ipsf.drawKImage(
            dtype=numpy.float64,
            nx=self.dim,
            ny=self.dim,
            scale=self.dk,
        )
Example #13
0
def InterpolatedMoments(p,
                        q,
                        img,
                        oversampled_psf_image,
                        gauss_sigma,
                        gauss_centroid=None,
                        gauss_g1=0.,
                        gauss_g2=0.,
                        x_interpolant='exact',
                        k_interpolant='lanczos64'):
    weight = galsim.Image(np.zeros_like(img.array))
    gauss = galsim.Gaussian(sigma=gauss_sigma * pixel_scale).shear(g1=gauss_g1,
                                                                   g2=gauss_g2)
    if gauss_centroid is None:
        gauss_centroid = img.true_center

    weight = gauss.drawImage(image=weight,
                             scale=pixel_scale,
                             method='no_pixel',
                             use_true_center=True,
                             offset=(gauss_centroid - img.true_center))

    if x_interpolant == 'exact':
        x = np.arange(img.xmin - gauss_centroid.x,
                      img.xmax - gauss_centroid.x + 0.1, 1.)
        y = np.arange(img.ymin - gauss_centroid.y,
                      img.ymax - gauss_centroid.y + 0.1, 1.)
        X, Y = np.meshgrid(x, y)

        XY = np.vstack([X.flatten(), Y.flatten()])
        A = np.array([[1. + gauss_g1, +gauss_g2], [+gauss_g2, 1. - gauss_g1]
                      ]) / (1 - (gauss_g1**2 + gauss_g2**2))  # | det A | = 1
        XYsheared = np.dot(A, XY)
        Xsheared, Ysheared = XYsheared[0, :].reshape(
            X.shape), XYsheared[1, :].reshape(Y.shape)

        monomial = 1.
        for pp in xrange(p):
            monomial *= Xsheared
        for qq in xrange(q):
            monomial *= Ysheared

        U = -0.5 * (Xsheared**2 + Ysheared**2) / gauss_sigma**2
        resampled_generalised_weight = galsim.Image(
            monomial * np.exp(U)) / (2. * np.pi * gauss_sigma**2)
    else:
        x = np.arange(
            img.xmin - img.center.x * 0 - gauss_centroid.x * 1, img.xmax -
            img.center.x * 0 - gauss_centroid.x * 1 + 0.1 / oversampling,
            1. / oversampling)
        y = np.arange(
            img.ymin - img.center.y * 0 - gauss_centroid.y * 1, img.ymax -
            img.center.y * 0 - gauss_centroid.y * 1 + 0.1 / oversampling,
            1. / oversampling)
        X, Y = np.meshgrid(x, y)

        assert len(x) == len(y)
        assert X.shape[0] == len(x)
        assert X.shape[1] == len(y)

        oversampled_weight = galsim.Image(np.zeros((len(x), len(y))))
        oversampled_weight = gauss.drawImage(
            image=oversampled_weight,
            scale=pixel_scale / oversampling,
            method='no_pixel',
            use_true_center=True,
            offset=(gauss_centroid - img.true_center) * (1. / oversampling))

        monomial = 1.
        for pp in xrange(p):
            monomial *= X
        for qq in xrange(q):
            monomial *= Y

        if not ((gauss_g1 == 0.) and (gauss_g2 == 0.)):
            iPSF = galsim.InterpolatedImage(oversampled_psf_image,
                                            x_interpolant=x_interpolant,
                                            k_interpolant=k_interpolant,
                                            gsparams=gsp)
            invPSF = galsim.Deconvolve(iPSF)
            sheared_psf = iPSF.shear(g1=gauss_g1, g2=gauss_g2)

        generalised_weight_image = galsim.Image(
            monomial * oversampled_weight.array,
            scale=pixel_scale / oversampling)
        generalised_weight_func = galsim.InterpolatedImage(
            generalised_weight_image,
            x_interpolant=x_interpolant,
            k_interpolant=k_interpolant,
            gsparams=gsp)
        sheared_generalised_weight_func = generalised_weight_func.shear(
            g1=gauss_g1, g2=gauss_g2)

        # Currently not doing the PSF correction. Instead, changing the PSF in the target image
        #if (gauss_g1==0.) and (gauss_g2==0.):
        #    sheared_generalised_weight_psf = sheared_generalised_weight_func
        #else:
        #    sheared_generalised_weight_psf = galsim.Convolve([sheared_generalised_weight_func, sheared_psf, invPSF])
        sheared_generalised_weight_psf = sheared_generalised_weight_func

        resampled_generalised_weight = galsim.Image(np.zeros_like(
            weight.array))
        resampled_generalised_weight = sheared_generalised_weight_psf.drawImage(
            image=resampled_generalised_weight,
            scale=pixel_scale,
            method='no_pixel',
            use_true_center=True,
            offset=(gauss_centroid - img.true_center))

    Q00 = np.sum(weight.array * img.array)
    try:
        Qpq = np.sum(resampled_generalised_weight.array * img.array)  #/Q00
        #fig, ax = plt.subplots(1,2)
        #ax[0].imshow(generalised_weight_image.array)
        #ax[1].imshow(resampled_generalised_weight.array)

        return Qpq
    except:
        print len(x), X.shape, x[0], x[-1], X[0, 0], X[
            -1, -1], img.array.shape, resampled_generalised_weight.array.shape
        return -99
Example #14
0
def test_deconvolve():
    """Test that deconvolution works as expected
    """
    dx = 0.4
    myImg1 = galsim.ImageF(80, 80, scale=dx)
    myImg1.setCenter(0, 0)
    myImg2 = galsim.ImageF(80, 80, scale=dx)
    myImg2.setCenter(0, 0)

    psf = galsim.Moffat(beta=3.8, fwhm=1.3, flux=5)
    inv_psf = galsim.Deconvolve(psf)
    psf.drawImage(myImg1, method='no_pixel')
    conv = galsim.Convolve(psf, psf, inv_psf)
    conv.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
        myImg1.array,
        myImg2.array,
        4,
        err_msg="Image of Deconvolve * obj^2 doesn't match obj alone")

    cen = galsim.PositionD(0, 0)
    np.testing.assert_equal(inv_psf.centroid, cen)
    np.testing.assert_almost_equal(inv_psf.flux, 1. / psf.flux)
    # This doesn't really have any meaning, but this is what we've assigned to a deconvolve max_sb.
    np.testing.assert_almost_equal(inv_psf.max_sb, -psf.max_sb / psf.flux**2)

    check_basic(inv_psf, "Deconvolve(Moffat)", do_x=False)

    # Also check Deconvolve with an asymmetric profile.
    obj1 = galsim.Gaussian(sigma=3., flux=4).shift(-0.2, -0.4)
    obj2 = galsim.Gaussian(sigma=6., flux=1.3).shift(0.3, 0.3)
    obj = galsim.Add(obj1, obj2)
    inv_obj = galsim.Deconvolve(obj)
    conv = galsim.Convolve([inv_obj, obj, obj])
    conv.drawImage(myImg1, method='no_pixel')
    obj.drawImage(myImg2, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
        myImg1.array,
        myImg2.array,
        4,
        err_msg=
        "Image of Deconvolve of asymmetric sum of Gaussians doesn't match obj alone"
    )

    np.testing.assert_equal(inv_obj.centroid, -obj.centroid)
    np.testing.assert_almost_equal(inv_obj.flux, 1. / obj.flux)
    np.testing.assert_almost_equal(inv_obj.max_sb, -obj.max_sb / obj.flux**2)

    check_basic(inv_obj, "Deconvolve(asym)", do_x=False)

    # Check picklability
    do_pickle(inv_obj)

    # And a significantly transformed deconvolve object
    jac = (0.3, -0.8, -0.7, 0.4)
    transformed_obj = obj.transform(*jac)
    transformed_inv_obj = inv_obj.transform(*jac)
    # Fix the flux -- most of the transformation commutes with deconvolution, but not flux scaling
    transformed_inv_obj /= transformed_obj.flux * transformed_inv_obj.flux
    check_basic(transformed_inv_obj,
                "transformed Deconvolve(asym)",
                do_x=False)
    conv = galsim.Convolve([transformed_inv_obj, transformed_obj, obj])
    conv.drawImage(myImg1, method='no_pixel')
    printval(myImg1, myImg2)
    np.testing.assert_array_almost_equal(
        myImg1.array,
        myImg2.array,
        4,
        err_msg="Transformed Deconvolve didn't cancel transformed original")

    np.testing.assert_equal(transformed_inv_obj.centroid,
                            -transformed_obj.centroid)
    np.testing.assert_almost_equal(transformed_inv_obj.flux,
                                   1. / transformed_obj.flux)
    np.testing.assert_almost_equal(
        transformed_inv_obj.max_sb,
        -transformed_obj.max_sb / transformed_obj.flux**2)

    check_basic(transformed_inv_obj,
                "transformed Deconvolve(asym)",
                do_x=False)

    # Check picklability
    do_pickle(transformed_inv_obj)

    # Should raise an exception for invalid arguments
    assert_raises(TypeError, galsim.Deconvolve)
    assert_raises(TypeError, galsim.Deconvolve, myImg1)
    assert_raises(TypeError, galsim.Deconvolve, [psf])
    assert_raises(TypeError, galsim.Deconvolve, psf, psf)
    assert_raises(TypeError, galsim.Deconvolve, psf, real_space=False)
    assert_raises(TypeError, galsim.Deconvolution)
    assert_raises(TypeError, galsim.Deconvolution, myImg1)
    assert_raises(TypeError, galsim.Deconvolution, [psf])
    assert_raises(TypeError, galsim.Deconvolution, psf, psf)
    assert_raises(TypeError, galsim.Deconvolution, psf, real_space=False)

    assert_raises(NotImplementedError, inv_obj.xValue, galsim.PositionD(0, 0))
    assert_raises(NotImplementedError, inv_obj.drawReal, myImg1)
    assert_raises(NotImplementedError, inv_obj.shoot, 1)
Example #15
0
def test_realspace_convolve():
    """Test the real-space convolution of a Moffat and a Box profile against a known result.
    """
    dx = 0.2
    # Note: Using an image created from Maple "exact" calculations.
    saved_img = galsim.fits.read(os.path.join(imgdir, "moffat_pixel.fits"))
    img = galsim.ImageF(saved_img.bounds, scale=dx)
    img.setCenter(0, 0)

    # Code was formerly:
    # psf = galsim.Moffat(beta=1.5, truncationFWHM=4, flux=1, half_light_radius=1)
    #
    # ...but this is no longer quite so simple since we changed the handling of trunc to be in
    # physical units.  However, the same profile can be constructed using
    # fwhm=1.0927449310213702,
    # as calculated by interval bisection in devutils/external/calculate_moffat_radii.py
    fwhm_backwards_compatible = 1.0927449310213702
    psf = galsim.Moffat(beta=1.5,
                        half_light_radius=1,
                        trunc=4 * fwhm_backwards_compatible,
                        flux=1)
    #psf = galsim.Moffat(beta=1.5, fwhm=fwhm_backwards_compatible,
    #trunc=4*fwhm_backwards_compatible, flux=1)
    pixel = galsim.Pixel(scale=dx, flux=1.)
    conv = galsim.Convolve([psf, pixel], real_space=True)
    conv.drawImage(img, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        img.array,
        saved_img.array,
        5,
        err_msg=
        "Using GSObject Convolve([psf,pixel]) disagrees with expected result")

    # Check with default_params
    conv = galsim.Convolve([psf, pixel],
                           real_space=True,
                           gsparams=default_params)
    conv.drawImage(img, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        img.array,
        saved_img.array,
        5,
        err_msg=
        "Using GSObject Convolve([psf,pixel]) with default_params disagrees with "
        "expected result")
    conv = galsim.Convolve([psf, pixel],
                           real_space=True,
                           gsparams=galsim.GSParams())
    conv.drawImage(img, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        img.array,
        saved_img.array,
        5,
        err_msg=
        "Using GSObject Convolve([psf,pixel]) with GSParams() disagrees with "
        "expected result")

    # Other ways to do the convolution:
    conv = galsim.Convolve(psf, pixel, real_space=True)
    conv.drawImage(img, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        img.array,
        saved_img.array,
        5,
        err_msg=
        "Using GSObject Convolve(psf,pixel) disagrees with expected result")

    # The real-space convolution algorithm is not (trivially) independent of the order of
    # the two things being convolved.  So check the opposite order.
    conv = galsim.Convolve([pixel, psf], real_space=True)
    conv.drawImage(img, scale=dx, method="sb", use_true_center=False)
    np.testing.assert_array_almost_equal(
        img.array,
        saved_img.array,
        5,
        err_msg=
        "Using GSObject Convolve([pixel,psf]) disagrees with expected result")

    check_basic(conv, "Truncated Moffat*Box", approx_maxsb=True)

    # Test kvalues
    do_kvalue(conv, img, "Truncated Moffat*Box")

    # Check picklability
    do_pickle(conv, lambda x: x.drawImage(method='sb'))
    do_pickle(conv)

    # Check some warnings that should be raised
    # More than 2 with only hard edges gives a warning either way. (Different warnings though.)
    assert_warns(galsim.GalSimWarning, galsim.Convolve, [psf, psf, pixel])
    assert_warns(galsim.GalSimWarning,
                 galsim.Convolve, [psf, psf, pixel],
                 real_space=False)
    assert_warns(galsim.GalSimWarning,
                 galsim.Convolve, [psf, psf, pixel],
                 real_space=True)
    # 2 with hard edges gives a warning if we ask it not to use real_space
    assert_warns(galsim.GalSimWarning,
                 galsim.Convolve, [psf, pixel],
                 real_space=False)
    # >2 of any kind give a warning if we ask it to use real_space
    g = galsim.Gaussian(sigma=2)
    assert_warns(galsim.GalSimWarning,
                 galsim.Convolve, [g, g, g],
                 real_space=True)
    # non-analytic profiles cannot do real_space
    d = galsim.Deconvolve(galsim.Gaussian(sigma=2))
    assert_warns(galsim.GalSimWarning,
                 galsim.Convolve, [g, d],
                 real_space=True)
    assert_raises(TypeError, galsim.Convolve, [g, d], real_space='true')

    # Repeat some of the above for AutoConvolve and AutoCorrelate
    conv = galsim.AutoConvolve(psf, real_space=True)
    check_basic(conv, "AutoConvolve Truncated Moffat", approx_maxsb=True)
    do_kvalue(conv, img, "AutoConvolve Truncated Moffat")
    do_pickle(conv)

    conv = galsim.AutoCorrelate(psf, real_space=True)
    check_basic(conv, "AutoCorrelate Truncated Moffat", approx_maxsb=True)
    do_kvalue(conv, img, "AutoCorrelate Truncated Moffat")
    do_pickle(conv)

    assert_warns(galsim.GalSimWarning,
                 galsim.AutoConvolve,
                 psf,
                 real_space=False)
    assert_warns(galsim.GalSimWarning, galsim.AutoConvolve, d, real_space=True)
    assert_warns(galsim.GalSimWarning,
                 galsim.AutoCorrelate,
                 psf,
                 real_space=False)
    assert_warns(galsim.GalSimWarning,
                 galsim.AutoCorrelate,
                 d,
                 real_space=True)
    assert_raises(TypeError, galsim.AutoConvolve, d, real_space='true')
    assert_raises(TypeError, galsim.AutoCorrelate, d, real_space='true')
Example #16
0
def test_convolve():
    """Test the convolution of a Moffat and a Box profile against a known result.
    """
    dx = 0.2
    # Using an exact Maple calculation for the comparison.  Only accurate to 4 decimal places.
    savedImg = galsim.fits.read(os.path.join(imgdir, "moffat_pixel.fits"))
    myImg = galsim.ImageF(savedImg.bounds, scale=dx)
    myImg.setCenter(0, 0)

    # Code was formerly:
    # psf = galsim.Moffat(beta=1.5, truncationFWHM=4, flux=1, half_light_radius=1)
    #
    # ...but this is no longer quite so simple since we changed the handling of trunc to be in
    # physical units.  However, the same profile can be constructed using
    # fwhm=1.0927449310213702,
    # as calculated by interval bisection in devutils/external/calculate_moffat_radii.py
    fwhm_backwards_compatible = 1.0927449310213702
    psf = galsim.Moffat(beta=1.5,
                        fwhm=fwhm_backwards_compatible,
                        trunc=4 * fwhm_backwards_compatible,
                        flux=1)
    pixel = galsim.Pixel(scale=dx, flux=1.)
    # Note: Since both of these have hard edges, GalSim wants to do this with real_space=True.
    # Here we are intentionally tesing the Fourier convolution, so we want to suppress the
    # warning that GalSim emits.
    with assert_warns(galsim.GalSimWarning):
        # We'll do the real space convolution below
        conv = galsim.Convolve([psf, pixel], real_space=False)
        conv.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
        np.testing.assert_array_almost_equal(
            myImg.array,
            savedImg.array,
            4,
            err_msg="Moffat convolved with Pixel disagrees with expected result"
        )
        assert psf.gsparams is galsim.GSParams.default
        assert pixel.gsparams is galsim.GSParams.default
        assert conv.gsparams is galsim.GSParams.default

        # Other ways to do the convolution:
        conv = galsim.Convolve(psf, pixel, real_space=False)
        conv.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
        np.testing.assert_array_almost_equal(
            myImg.array,
            savedImg.array,
            4,
            err_msg=
            "Using GSObject Convolve(psf,pixel) disagrees with expected result"
        )
        assert conv.gsparams is galsim.GSParams.default

        # Check with default_params
        conv = galsim.Convolve([psf, pixel],
                               real_space=False,
                               gsparams=default_params)
        conv.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
        np.testing.assert_array_almost_equal(
            myImg.array,
            savedImg.array,
            4,
            err_msg=
            "Using GSObject Convolve([psf,pixel]) with default_params disagrees with"
            "expected result")
        # In this case, it's not the same object, but it should be ==
        assert conv.gsparams is not galsim.GSParams.default
        assert conv.gsparams == galsim.GSParams.default
        assert conv.gsparams is default_params
        # Also the components shouldn't have changed.
        assert conv.obj_list[0] is psf
        assert conv.obj_list[1] is pixel

        conv = galsim.Convolve([psf, pixel],
                               real_space=False,
                               gsparams=galsim.GSParams())
        conv.drawImage(myImg, scale=dx, method="sb", use_true_center=False)
        np.testing.assert_array_almost_equal(
            myImg.array,
            savedImg.array,
            4,
            err_msg=
            "Using GSObject Convolve([psf,pixel]) with GSParams() disagrees with"
            "expected result")
        assert conv.gsparams is not galsim.GSParams.default
        assert conv.gsparams == galsim.GSParams.default

    cen = galsim.PositionD(0, 0)
    np.testing.assert_equal(conv.centroid, cen)
    np.testing.assert_almost_equal(conv.flux, psf.flux * pixel.flux)
    # Not almost_equal.  Convolutions don't give a very good estimate.
    # They are almost always too high, which is actually ok for how we use max_sb for phot shooting.
    np.testing.assert_array_less(conv.xValue(cen), conv.max_sb)

    check_basic(conv, "Moffat * Pixel")

    # Test photon shooting.
    with assert_warns(galsim.GalSimWarning):
        do_shoot(conv, myImg, "Moffat * Pixel")

    # Convolution of just one argument should be equivalent to that argument.
    single = galsim.Convolve(psf)
    gsobject_compare(single, psf)
    check_basic(single, "`convolution' of single Moffat")
    do_pickle(single)
    do_shoot(single, myImg, "single Convolution")

    single = galsim.Convolve([psf])
    gsobject_compare(single, psf)
    check_basic(single, "`convolution' of single Moffat")
    do_pickle(single)

    single = galsim.Convolution(psf)
    gsobject_compare(single, psf)
    check_basic(single, "`convolution' of single Moffat")
    do_pickle(single)

    single = galsim.Convolution([psf])
    gsobject_compare(single, psf)
    check_basic(single, "`convolution' of single Moffat")
    do_pickle(single)

    # Should raise an exception for invalid arguments
    assert_raises(TypeError, galsim.Convolve)
    assert_raises(TypeError, galsim.Convolve, myImg)
    assert_raises(TypeError, galsim.Convolve, [myImg])
    assert_raises(TypeError, galsim.Convolve, [psf, myImg])
    assert_raises(TypeError, galsim.Convolve, [psf, psf, myImg])
    assert_raises(TypeError, galsim.Convolve, [psf, psf], realspace=False)
    assert_raises(TypeError, galsim.Convolution)
    assert_raises(TypeError, galsim.Convolution, myImg)
    assert_raises(TypeError, galsim.Convolution, [myImg])
    assert_raises(TypeError, galsim.Convolution, [psf, myImg])
    assert_raises(TypeError, galsim.Convolution, [psf, psf, myImg])
    assert_raises(TypeError, galsim.Convolution, [psf, psf], realspace=False)

    with assert_warns(galsim.GalSimWarning):
        triple = galsim.Convolve(psf, psf, pixel)
    assert_raises(galsim.GalSimError, triple.xValue, galsim.PositionD(0, 0))
    assert_raises(galsim.GalSimError, triple.drawReal, myImg)

    deconv = galsim.Convolve(psf, galsim.Deconvolve(pixel))
    assert_raises(galsim.GalSimError, deconv.xValue, galsim.PositionD(0, 0))
    assert_raises(galsim.GalSimError, deconv.drawReal, myImg)
    assert_raises(galsim.GalSimError, deconv.drawPhot, myImg, n_photons=10)
    assert_raises(galsim.GalSimError, deconv.makePhot, n_photons=10)
Example #17
0
def test_gsparams():
    """Test withGSParams with some non-default gsparams
    """
    obj1 = galsim.Exponential(half_light_radius=1.7)
    obj2 = galsim.Pixel(scale=0.2)
    gsp = galsim.GSParams(folding_threshold=1.e-4,
                          maxk_threshold=1.e-4,
                          maximum_fft_size=1.e4)
    gsp2 = galsim.GSParams(folding_threshold=1.e-2, maxk_threshold=1.e-2)

    # Convolve
    conv = galsim.Convolve(obj1, obj2)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp

    conv2 = galsim.Convolve(obj1.withGSParams(gsp), obj2.withGSParams(gsp))
    conv3 = galsim.Convolve(
        galsim.Exponential(half_light_radius=1.7, gsparams=gsp),
        galsim.Pixel(scale=0.2))
    conv4 = galsim.Convolve(obj1, obj2, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ', conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ', conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(obj1, obj2, gsparams=gsp, propagate_gsparams=False)
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == galsim.GSParams()
    assert conv5.obj_list[1].gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == galsim.GSParams()
    assert conv6.obj_list[1].gsparams == galsim.GSParams()

    # AutoConvolve
    conv = galsim.AutoConvolve(obj1)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.orig_obj.gsparams == gsp

    conv2 = galsim.AutoConvolve(obj1.withGSParams(gsp))
    conv3 = galsim.AutoConvolve(obj1, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    print('stepk = ', conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ', conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv4 = galsim.AutoConvolve(obj1, gsparams=gsp, propagate_gsparams=False)
    assert conv4 != conv3
    assert conv4.gsparams == gsp
    assert conv4.orig_obj.gsparams == galsim.GSParams()

    conv5 = conv4.withGSParams(gsp2)
    assert conv5 != conv4
    assert conv5.gsparams == gsp2
    assert conv5.orig_obj.gsparams == galsim.GSParams()

    # AutoCorrelate
    conv = galsim.AutoCorrelate(obj1)
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.orig_obj.gsparams == gsp

    conv2 = galsim.AutoCorrelate(obj1.withGSParams(gsp))
    conv3 = galsim.AutoCorrelate(obj1, gsparams=gsp)
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    print('stepk = ', conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ', conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv4 = galsim.AutoCorrelate(obj1, gsparams=gsp, propagate_gsparams=False)
    assert conv4 != conv3
    assert conv4.gsparams == gsp
    assert conv4.orig_obj.gsparams == galsim.GSParams()

    conv5 = conv4.withGSParams(gsp2)
    assert conv5 != conv4
    assert conv5.gsparams == gsp2
    assert conv5.orig_obj.gsparams == galsim.GSParams()

    # Deconvolve
    conv = galsim.Convolve(obj1, galsim.Deconvolve(obj2))
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp
    assert conv1.obj_list[1].orig_obj.gsparams == gsp

    conv2 = galsim.Convolve(obj1, galsim.Deconvolve(obj2.withGSParams(gsp)))
    conv3 = galsim.Convolve(obj1.withGSParams(gsp), galsim.Deconvolve(obj2))
    conv4 = galsim.Convolve(obj1, galsim.Deconvolve(obj2, gsparams=gsp))
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ', conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ', conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(
        obj1, galsim.Deconvolve(obj2, gsparams=gsp, propagate_gsparams=False))
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == gsp
    assert conv5.obj_list[1].gsparams == gsp
    assert conv5.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == gsp2
    assert conv6.obj_list[1].gsparams == gsp2
    assert conv6.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    # FourierSqrt
    conv = galsim.Convolve(obj1, galsim.FourierSqrt(obj2))
    conv1 = conv.withGSParams(gsp)
    assert conv.gsparams == galsim.GSParams()
    assert conv1.gsparams == gsp
    assert conv1.obj_list[0].gsparams == gsp
    assert conv1.obj_list[1].gsparams == gsp
    assert conv1.obj_list[1].orig_obj.gsparams == gsp

    conv2 = galsim.Convolve(obj1, galsim.FourierSqrt(obj2.withGSParams(gsp)))
    conv3 = galsim.Convolve(obj1.withGSParams(gsp), galsim.FourierSqrt(obj2))
    conv4 = galsim.Convolve(obj1, galsim.FourierSqrt(obj2, gsparams=gsp))
    assert conv != conv1
    assert conv1 == conv2
    assert conv1 == conv3
    assert conv1 == conv4
    print('stepk = ', conv.stepk, conv1.stepk)
    assert conv1.stepk < conv.stepk
    print('maxk = ', conv.maxk, conv1.maxk)
    assert conv1.maxk > conv.maxk

    conv5 = galsim.Convolve(
        obj1, galsim.FourierSqrt(obj2, gsparams=gsp, propagate_gsparams=False))
    assert conv5 != conv4
    assert conv5.gsparams == gsp
    assert conv5.obj_list[0].gsparams == gsp
    assert conv5.obj_list[1].gsparams == gsp
    assert conv5.obj_list[1].orig_obj.gsparams == galsim.GSParams()

    conv6 = conv5.withGSParams(gsp2)
    assert conv6 != conv5
    assert conv6.gsparams == gsp2
    assert conv6.obj_list[0].gsparams == gsp2
    assert conv6.obj_list[1].gsparams == gsp2
    assert conv6.obj_list[1].orig_obj.gsparams == galsim.GSParams()
    lam_over_diam_cosmos = (814.e-9 /
                            2.4) * (180. / np.pi) * 3600.  # ~lamda/D in arcsec
    lam_over_diam_ground = lam_over_diam_cosmos * 2.4 / 4.  # Generic 4m at same lambda
    psf_cosmos = galsim.Convolve([
        galsim.Airy(lam_over_diam=lam_over_diam_cosmos, obscuration=0.4),
        galsim.Pixel(0.05)
    ])
    psf_ground = galsim.Convolve([
        galsim.Kolmogorov(fwhm=0.8),
        galsim.Pixel(0.18),
        galsim.OpticalPSF(lam_over_diam=lam_over_diam_ground,
                          coma2=0.4,
                          defocus=-0.6)
    ])
    psf_shera = galsim.Convolve([
        psf_ground, (galsim.Deconvolve(psf_cosmos)).createSheared(g1=0.03,
                                                                  g2=-0.01)
    ])
    # Then define the convolved cosmos correlated noise model
    conv_cn = cn.copy()
    conv_cn.convolveWith(psf_shera)
    # Then draw the correlation function for this correlated noise as the reference
    refim = galsim.ImageD(smallim_size, smallim_size)
    conv_cn.draw(refim, dx=0.18)
    # Now start the tests...
    #
    # First we generate a COSMOS noise field (cosimage), read it into an InterpolatedImage and
    # then convolve it with psf

    size_factor = .25  # scale the sizes, need size_factor * largeim_size to be an integer
    interp = galsim.Linear(
Example #19
0
    def __init__(self, real_galaxy_catalog, index=None, id=None, random=False,
                 rng=None, x_interpolant=None, k_interpolant=None, flux=None, pad_factor=4,
                 noise_pad_size=0, gsparams=None, logger=None):

        import numpy as np

        if rng is None:
            rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError("The rng provided to RealGalaxy constructor is not a BaseDeviate")
 
        # Code block below will be for galaxy selection; not all are currently implemented.  Each
        # option must return an index within the real_galaxy_catalog.        
        if index is not None:
            if id is not None or random is True:
                raise AttributeError('Too many methods for selecting a galaxy!')
            use_index = index
        elif id is not None:
            if random is True:
                raise AttributeError('Too many methods for selecting a galaxy!')
            use_index = real_galaxy_catalog.getIndexForID(id)
        elif random is True:
            uniform_deviate = galsim.UniformDeviate(rng)
            use_index = int(real_galaxy_catalog.nobjects * uniform_deviate()) 
        else:
            raise AttributeError('No method specified for selecting a galaxy!')

        if logger:
            logger.debug('RealGalaxy %d: Start RealGalaxy constructor.',use_index)


        # read in the galaxy, PSF images; for now, rely on pyfits to make I/O errors.
        self.gal_image = real_galaxy_catalog.getGal(use_index)
        if logger:
            logger.debug('RealGalaxy %d: Got gal_image',use_index)

        self.psf_image = real_galaxy_catalog.getPSF(use_index)
        if logger:
            logger.debug('RealGalaxy %d: Got psf_image',use_index)

        #self.noise = real_galaxy_catalog.getNoise(use_index, rng, gsparams)
        # This is a duplication of the RealGalaxyCatalog.getNoise() function, since we
        # want it to be possible to have the RealGalaxyCatalog in another process, and the
        # BaseCorrelatedNoise object is not picklable.  So we just build it here instead.
        noise_image, pixel_scale, var = real_galaxy_catalog.getNoiseProperties(use_index)
        if logger:
            logger.debug('RealGalaxy %d: Got noise_image',use_index)

        if noise_image is None:
            self.noise = galsim.UncorrelatedNoise(var, rng=rng, scale=pixel_scale, gsparams=gsparams)
        else:
            ii = galsim.InterpolatedImage(noise_image, scale=pixel_scale, normalization="sb",
                                          calculate_stepk=False, calculate_maxk=False,
                                          x_interpolant='linear', gsparams=gsparams)
            self.noise = galsim.correlatednoise._BaseCorrelatedNoise(rng, ii)
            self.noise = self.noise.withVariance(var)
        if logger:
            logger.debug('RealGalaxy %d: Finished building noise',use_index)

        # Save any other relevant information as instance attributes
        self.catalog_file = real_galaxy_catalog.getFileName()
        self.index = use_index
        self.pixel_scale = float(pixel_scale)

        # Convert noise_pad to the right noise to pass to InterpolatedImage
        if noise_pad_size:
            noise_pad = self.noise
        else:
            noise_pad = 0.

        # Build the InterpolatedImage of the PSF.
        self.original_psf = galsim.InterpolatedImage(
            self.psf_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant, 
            flux=1.0, scale=self.pixel_scale, gsparams=gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Made original_psf',use_index)

        # Build the InterpolatedImage of the galaxy.
        # Use the stepK() value of the PSF as a maximum value for stepK of the galaxy.
        # (Otherwise, low surface brightness galaxies can get a spuriously high stepk, which
        # leads to problems.)
        self.original_image = galsim.InterpolatedImage(
                self.gal_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant,
                scale=self.pixel_scale, pad_factor=pad_factor, noise_pad_size=noise_pad_size,
                calculate_stepk=self.original_psf.stepK(),
                calculate_maxk=self.original_psf.maxK(),
                noise_pad=noise_pad, rng=rng, gsparams=gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Made original_image',use_index)

        # If flux is None, leave flux as given by original image
        if flux is not None:
            self.original_image = self.original_image.withFlux(flux)

        # Calculate the PSF "deconvolution" kernel
        psf_inv = galsim.Deconvolve(self.original_psf, gsparams=gsparams)
        # Initialize the SBProfile attribute
        GSObject.__init__(
            self, galsim.Convolve([self.original_image, psf_inv], gsparams=gsparams))
        if logger:
            logger.debug('RealGalaxy %d: Made gsobject',use_index)

        # Save the noise in the image as an accessible attribute
        self.noise = self.noise.convolvedWith(psf_inv, gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Finished building RealGalaxy',use_index)
Example #20
0
    def __init__(self, real_galaxy_catalog, index=None, id=None, random=False,
                 rng=None, x_interpolant=None, k_interpolant=None, flux=None, pad_factor=0,
                 noise_pad=False, pad_image=None, use_cache=True, gsparams=None):

        import pyfits
        import numpy as np

        if rng is None:
            rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError("The rng provided to RealGalaxy constructor is not a BaseDeviate")
 
        # Code block below will be for galaxy selection; not all are currently implemented.  Each
        # option must return an index within the real_galaxy_catalog.        
        if index is not None:
            if id is not None or random is True:
                raise AttributeError('Too many methods for selecting a galaxy!')
            use_index = index
        elif id is not None:
            if random is True:
                raise AttributeError('Too many methods for selecting a galaxy!')
            use_index = real_galaxy_catalog._get_index_for_id(id)
        elif random is True:
            uniform_deviate = galsim.UniformDeviate(rng)
            use_index = int(real_galaxy_catalog.nobjects * uniform_deviate()) 
        else:
            raise AttributeError('No method specified for selecting a galaxy!')

        # read in the galaxy, PSF images; for now, rely on pyfits to make I/O errors.
        gal_image = real_galaxy_catalog.getGal(use_index)
        PSF_image = real_galaxy_catalog.getPSF(use_index)
        noise = real_galaxy_catalog.getNoise(use_index, rng, gsparams)

        # save any other relevant information as instance attributes
        self.catalog_file = real_galaxy_catalog.file_name
        self.index = use_index
        self.pixel_scale = float(real_galaxy_catalog.pixel_scale[use_index])

        # handle noise-padding options
        try:
            noise_pad = galsim.config.value._GetBoolValue(noise_pad,'')
            # If it's a bool and True, use the correlated noise specified in the catalog.
            if noise_pad:
                noise_pad = noise
            else:
                noise_pad = 0.
        except:
            # If it's not a bool, or convertible to a bool, leave it alone.
            pass

        self.original_image = galsim.InterpolatedImage(
                gal_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant,
                dx=self.pixel_scale, pad_factor=pad_factor, noise_pad=noise_pad, rng=rng,
                pad_image=pad_image, use_cache=use_cache, gsparams=gsparams)
        # If flux is None, leave flux as given by original image
        if flux != None:
            self.original_image.setFlux(flux)

        # also make the original PSF image, with far less fanfare: we don't need to pad with
        # anything interesting.
        self.original_PSF = galsim.InterpolatedImage(
            PSF_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant, 
            flux=1.0, dx=self.pixel_scale, gsparams=gsparams)
        #self.original_PSF.setFlux(1.0)

        # Calculate the PSF "deconvolution" kernel
        psf_inv = galsim.Deconvolve(self.original_PSF, gsparams=gsparams)
        # Initialize the SBProfile attribute
        GSObject.__init__(
            self, galsim.Convolve([self.original_image, psf_inv], gsparams=gsparams))

        # Save the noise in the image as an accessible attribute
        noise.convolveWith(psf_inv, gsparams)
        self.noise = noise
Example #21
0
    def __init__(self, real_galaxy_catalog, index=None, id=None, random=False,
                 rng=None, x_interpolant=None, k_interpolant=None, flux=None, flux_rescale=None,
                 pad_factor=4, noise_pad_size=0, gsparams=None, logger=None):


        if rng is None:
            self.rng = galsim.BaseDeviate()
        elif not isinstance(rng, galsim.BaseDeviate):
            raise TypeError("The rng provided to RealGalaxy constructor is not a BaseDeviate")
        else:
            self.rng = rng
        self._rng = self.rng.duplicate()  # This is only needed if we want to make sure eval(repr)
                                          # results in the same object.

        if flux is not None and flux_rescale is not None:
            raise TypeError("Cannot supply a flux and a flux rescaling factor!")

        if isinstance(real_galaxy_catalog, tuple):
            # Special (undocumented) way to build a RealGalaxy without needing the rgc directly
            # by providing the things we need from it.  Used by COSMOSGalaxy.
            self.gal_image, self.psf_image, noise_image, pixel_scale, var = real_galaxy_catalog
            use_index = 0  # For the logger statements below.
            if logger:
                logger.debug('RealGalaxy %d: Start RealGalaxy constructor.',use_index)
            self.catalog_file = None
        else:
            # Get the index to use in the catalog
            if index is not None:
                if id is not None or random is True:
                    raise AttributeError('Too many methods for selecting a galaxy!')
                use_index = index
            elif id is not None:
                if random is True:
                    raise AttributeError('Too many methods for selecting a galaxy!')
                use_index = real_galaxy_catalog.getIndexForID(id)
            elif random:
                ud = galsim.UniformDeviate(self.rng)
                use_index = int(real_galaxy_catalog.nobjects * ud())
                if hasattr(real_galaxy_catalog, 'weight'):
                    # If weight factors are available, make sure the random selection uses the
                    # weights to remove the catalog-level selection effects (flux_radius-dependent
                    # probability of making a postage stamp for a given object).
                    while ud() > real_galaxy_catalog.weight[use_index]:
                        # Pick another one to try.
                        use_index = int(real_galaxy_catalog.nobjects * ud())
            else:
                raise AttributeError('No method specified for selecting a galaxy!')
            if logger:
                logger.debug('RealGalaxy %d: Start RealGalaxy constructor.',use_index)

            # Read in the galaxy, PSF images; for now, rely on pyfits to make I/O errors.
            self.gal_image = real_galaxy_catalog.getGal(use_index)
            if logger:
                logger.debug('RealGalaxy %d: Got gal_image',use_index)

            self.psf_image = real_galaxy_catalog.getPSF(use_index)
            if logger:
                logger.debug('RealGalaxy %d: Got psf_image',use_index)

            #self.noise = real_galaxy_catalog.getNoise(use_index, self.rng, gsparams)
            # We need to duplication some of the RealGalaxyCatalog.getNoise() function, since we
            # want it to be possible to have the RealGalaxyCatalog in another process, and the
            # BaseCorrelatedNoise object is not picklable.  So we just build it here instead.
            noise_image, pixel_scale, var = real_galaxy_catalog.getNoiseProperties(use_index)
            if logger:
                logger.debug('RealGalaxy %d: Got noise_image',use_index)
            self.catalog_file = real_galaxy_catalog.getFileName()

        if noise_image is None:
            self.noise = galsim.UncorrelatedNoise(var, rng=self.rng, scale=pixel_scale,
                                                  gsparams=gsparams)
        else:
            ii = galsim.InterpolatedImage(noise_image, normalization="sb",
                                          calculate_stepk=False, calculate_maxk=False,
                                          x_interpolant='linear', gsparams=gsparams)
            self.noise = galsim.correlatednoise._BaseCorrelatedNoise(self.rng, ii, noise_image.wcs)
            self.noise = self.noise.withVariance(var)
        if logger:
            logger.debug('RealGalaxy %d: Finished building noise',use_index)

        # Save any other relevant information as instance attributes
        self.catalog = real_galaxy_catalog
        self.index = use_index
        self.pixel_scale = float(pixel_scale)
        self._x_interpolant = x_interpolant
        self._k_interpolant = k_interpolant
        self._pad_factor = pad_factor
        self._noise_pad_size = noise_pad_size
        self._flux = flux
        self._gsparams = gsparams

        # Convert noise_pad to the right noise to pass to InterpolatedImage
        if noise_pad_size:
            noise_pad = self.noise
        else:
            noise_pad = 0.

        # Build the InterpolatedImage of the PSF.
        self.original_psf = galsim.InterpolatedImage(
            self.psf_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant,
            flux=1.0, gsparams=gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Made original_psf',use_index)

        # Build the InterpolatedImage of the galaxy.
        # Use the stepK() value of the PSF as a maximum value for stepK of the galaxy.
        # (Otherwise, low surface brightness galaxies can get a spuriously high stepk, which
        # leads to problems.)
        self.original_gal = galsim.InterpolatedImage(
                self.gal_image, x_interpolant=x_interpolant, k_interpolant=k_interpolant,
                pad_factor=pad_factor, noise_pad_size=noise_pad_size,
                calculate_stepk=self.original_psf.stepK(),
                calculate_maxk=self.original_psf.maxK(),
                noise_pad=noise_pad, rng=self.rng, gsparams=gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Made original_gal',use_index)

        # If flux is None, leave flux as given by original image
        if flux is not None:
            flux_rescale = flux / self.original_gal.getFlux()
        if flux_rescale is not None:
            self.original_gal *= flux_rescale
            self.noise *= flux_rescale**2

        # Calculate the PSF "deconvolution" kernel
        psf_inv = galsim.Deconvolve(self.original_psf, gsparams=gsparams)

        # Initialize the SBProfile attribute
        GSObject.__init__(
            self, galsim.Convolve([self.original_gal, psf_inv], gsparams=gsparams))
        if logger:
            logger.debug('RealGalaxy %d: Made gsobject',use_index)

        # Save the noise in the image as an accessible attribute
        self.noise = self.noise.convolvedWith(psf_inv, gsparams)
        if logger:
            logger.debug('RealGalaxy %d: Finished building RealGalaxy',use_index)
Example #22
0
    def homogenize_image(self, image):
        """Homogenize the PSF of an image.

        Parameters
        ----------
        image : np.ndarray
            The image to which to apply the PSF homogenization.

        Returns
        -------
        himage : np.ndarray
            The PSF homogenized image.
        """
        assert self.image_shape == image.shape

        # offset to the center of each patch
        patch_size_2 = (self.patch_size - 1) / 2

        # this size is the patch size plus all of the pixels needed
        # to form at least one PSF image. thus we subtract 1 since the
        # patch_size will always be at least 1
        tot_size = self.patch_size + self._psf_im_shape[0] - 1

        # offset of central patch_size pixels in the output of the
        # homogenization convolution (of size tot_size)
        psf_size_2 = (self._psf_im_shape[0] - 1) // 2

        # output image is the same size as the input
        himage = np.zeros_like(image)

        # we pad the input with the zeros needed for both halves of the
        # psf image width
        pimage = np.pad(image, psf_size_2, 'constant')

        n_row_patches = self.image_shape[0] // self.patch_size
        n_col_patches = self.image_shape[1] // self.patch_size
        for row_patch in range(n_row_patches):
            # start of the patch in the final image and the buffered image
            row = row_patch * self.patch_size

            for col_patch in range(n_col_patches):
                col = col_patch * self.patch_size

                patch = galsim.InterpolatedImage(galsim.ImageD(
                    pimage[row:row + tot_size, col:col + tot_size].copy()),
                                                 wcs=galsim.PixelScale(1.0))

                psf_im = self.psf_model(row + patch_size_2, col + patch_size_2)
                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))

                hpatch = galsim.Convolve(patch, kern).drawImage(
                    nx=tot_size,
                    ny=tot_size,
                    wcs=galsim.PixelScale(1),
                    method='no_pixel')

                himage[row:row+self.patch_size, col:col+self.patch_size] \
                    = hpatch.array[psf_size_2:psf_size_2+self.patch_size,
                                   psf_size_2:psf_size_2+self.patch_size]

        return himage
Example #23
0
def getDiffKernel(P, P0, nx=50, ny=50, pixel_scale=1.):
    PD = galsim.Convolve(P, galsim.Deconvolve(P0))
    PDimg = PD.drawImage(nx=nx, ny=ny, scale=pixel_scale, method='no_pixel')
    return PDimg
Example #24
0
def test_metacal_tracking():
    """Test that the noise tracking works for the metacal use case involving deconvolution and
    reconvolution by almost the same PSF.

    This test is similar to the above test_uncorrelated_noise_tracking, except the modifications
    are based on what is done for the metacal procedure.
    """
    import math

    def check_noise(noise_image, noise, msg):
        # A helper function to check that the current noise in the image is properly described
        # by the given CorrelatedNoise object
        noise2 = galsim.CorrelatedNoise(noise_image)
        print('noise = ', noise)
        print('noise2 = ', noise2)
        np.testing.assert_almost_equal(noise.getVariance(),
                                       noise2.getVariance(),
                                       decimal=CHECKNOISE_NDECIMAL,
                                       err_msg=msg +
                                       ': variance does not match.')
        cf_im1 = galsim.Image(8, 8, wcs=noise_image.wcs)
        cf_im2 = cf_im1.copy()
        noise.drawImage(image=cf_im1)
        noise2.drawImage(image=cf_im2)
        np.testing.assert_almost_equal(cf_im1.array,
                                       cf_im2.array,
                                       decimal=CHECKNOISE_NDECIMAL,
                                       err_msg=msg +
                                       ': image of cf does not match.')

    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')

    seed = 1234567  # For use as a unit test, we need a specific seed
    #seed = 0  # During testing, it's useful to see how numbers flop around to know if
    # something is systematic or random.
    rng = galsim.BaseDeviate(seed)

    noise_var = 1.3
    im_size = 1024
    dg = 0.1  # This is bigger than metacal would use, but it makes the test easier.

    # Use a non-trivial wcs...
    #wcs = galsim.JacobianWCS(0.26, 0.04, -0.04, -0.26)  # Rotation + flip.  No shear.
    wcs = galsim.JacobianWCS(0.26, 0.03, 0.08, -0.21)  # Fully complex
    #dudx =  0.12*0.26
    #dudy =  1.10*0.26
    #dvdx = -0.915*0.26
    #dvdy = -0.04*0.26
    #wcs = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy)  # Even more extreme

    # And an asymmetric PSF
    #orig_psf = galsim.Gaussian(fwhm=0.9).shear(g1=0.05, g2=0.03)
    # This one is small enough not to be fully Nyquist sampled, which makes things harder.
    orig_psf = galsim.Gaussian(fwhm=0.7).shear(g1=0.05, g2=0.03)

    # pixel is the pixel in world coords
    pixel = wcs.toWorld(galsim.Pixel(scale=1))
    pixel_inv = galsim.Deconvolve(pixel)

    psf_image = orig_psf.drawImage(nx=im_size, ny=im_size, wcs=wcs)

    # Metacal only has access to the PSF as an image, so use this from here on.
    psf = galsim.InterpolatedImage(psf_image)
    psf_nopix = galsim.Convolve([psf, pixel_inv])
    psf_inv = galsim.Deconvolve(psf)

    # Not what is done currently, but using a smoother target PSF helps make sure the
    # zeros in the deconvolved PSF get adequately zeroed out.
    def get_target_psf(psf):
        dk = 0.1  # The resolution in k space for the KImage
        small_kval = 1.e-2  # Find the k where the given psf hits this kvalue
        smaller_kval = 3.e-3  # Target PSF will have this kvalue at the same k

        kim = psf.drawKImage(scale=dk)
        karr_r = kim.real.array
        # Find the smallest r where the kval < small_kval
        nk = karr_r.shape[0]
        kx, ky = np.meshgrid(np.arange(-nk / 2, nk / 2),
                             np.arange(-nk / 2, nk / 2))
        ksq = (kx**2 + ky**2) * dk**2
        ksq_max = np.min(ksq[karr_r < small_kval * psf.flux])

        # We take our target PSF to be the (round) Gaussian that is even smaller at this ksq
        # exp(-0.5 * ksq_max * sigma_sq) = smaller_kval
        sigma_sq = -2. * np.log(smaller_kval) / ksq_max
        return galsim.Gaussian(sigma=np.sqrt(sigma_sq))

    # The target PSF dilates the part without the pixel, but reconvolve by the real pixel.
    #psf_target_nopix = psf_nopix.dilate(1. + 2.*dg)
    #psf_target_nopix = orig_psf.dilate(1. + 4.*dg)
    psf_target_nopix = get_target_psf(psf_nopix.shear(g1=dg))
    print('PSF target HLR = ', psf_target_nopix.calculateHLR())
    print('PSF target FWHM = ', psf_target_nopix.calculateFWHM())
    psf_target = galsim.Convolve([psf_target_nopix, pixel])

    # Make an image of pure (white) Gaussian noise
    # Normally, there would be a galaxy in this image, but for the tests, we just have noise.
    obs_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs)
    obs_image.addNoise(
        galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var)))

    # The noise on this image should be describable as an UncorrelatedNoise object:
    noise = galsim.UncorrelatedNoise(variance=noise_var, wcs=wcs)
    check_noise(obs_image, noise, 'initial UncorrelatedNoise model is wrong')

    # Make an InterpolatedImage profile to use for manipulating this image
    # We can get away with no padding here, since our image is so large, but normally, you would
    # probably want to pad this with noise padding.
    ii = galsim.InterpolatedImage(obs_image, pad_factor=1)
    ii.noise = noise

    # If we draw it back, the attached noise attribute should still be correct
    check_noise(ii.drawImage(obs_image.copy(), method='no_pixel'), ii.noise,
                'noise model is wrong for InterpolatedImage')

    # Here is the metacal process for which we want to understand the noise.
    # We'll try a few different methods.
    shear = galsim.Shear(g1=dg)
    sheared_obj = galsim.Convolve(ii, psf_inv).shear(shear)
    final_obj = galsim.Convolve(psf_target, sheared_obj)
    final_image = final_obj.drawImage(obs_image.copy(), method='no_pixel')

    try:
        check_symm_noise(final_image, 'Initial image')
        #didnt_fail = True
        # This bit doesn't work while we are not actually raising exceptions in check_symm_noise
        # So we expect to see **FAIL** text at this point.
        print(
            'The above tests are expected to **FAIL**.  This is not a problem.'
        )
        didnt_fail = False
    except AssertionError as e:
        print('As expected initial image fails symmetric noise test:')
        print(e)
        didnt_fail = False
    if didnt_fail:
        assert False, 'Initial image was expected to fail symmetric noise test, but passed.'

    if True:
        print('\n\nStrategy 1:')
        # Strategy 1: Use the noise attribute attached to ii and use it to either whiten or
        #             symmetrize the noise in the final image.
        # Note: The check_noise tests fail.  I think because the convolve and deconvolve impose
        #       a maxk = that of the psf.  Which is too small for an accurate rendering of the
        #       correlation function (here just an autocorrelation of a Pixel.
        # The whiten tests kind of work, but they add a lot of extra noise.  Much more than
        # strategy 4 below.  So the level of correlation remaining is pretty well below the
        # dc variance.  Symmetrize doesn't add much noise, but the residual correlation is about
        # the same, which means it doesn't pass the test relative to the lower dc variance.

        # First, deconvolve and reconvolve by the same PSF:
        test_obj = galsim.Convolve([ii, psf, psf_inv])
        # This fails...
        if False:
            check_noise(
                test_obj.drawImage(obs_image.copy(),
                                   method='no_pixel'), test_obj.noise,
                'noise model is wrong after convolve/deconvolve by psf')

        # Now use a slightly dilated PSF for the reconvolution:
        test_obj = galsim.Convolve([ii, psf_target, psf_inv])
        if False:
            check_noise(
                test_obj.drawImage(obs_image.copy(), method='no_pixel'),
                test_obj.noise, 'noise model is wrong for dilated target psf')

        # Finally, include the shear step.  This was done above with sheared_obj, final_obj.
        if False:
            check_noise(final_image, final_obj.noise,
                        'noise model is wrong when including small shear')

        # If we whiten using this noise model, we should get back to white noise.
        t3 = time.time()
        final_image2 = final_image.copy()  # Don't clobber the original
        final_var = final_image2.whitenNoise(final_obj.noise)
        t4 = time.time()
        print('Noise tracking method with whiten: final_var = ', final_var)
        print('Check: direct variance = ', np.var(final_image2.array))
        check_symm_noise(final_image2, 'noise whitening does not work')
        print('Time for noise tracking with whiten = ', t4 - t3)

        # Using symmetrizeNoise should add less noise, but also work.
        t3 = time.time()
        final_image2 = final_image.copy()
        final_var = final_image2.symmetrizeNoise(final_obj.noise)
        t4 = time.time()
        print('Noise tracking method with symmetrize: final_var = ', final_var)
        print('Check: direct variance = ', np.var(final_image2.array))
        check_symm_noise(final_image2, 'noise symmetrizing does not work')
        print('Time for noise tracking with symmetrize = ', t4 - t3)

    if True:
        print('\n\nStrategy 2:')
        # Strategy 2: Don't trust the noise tracking. Track a noise image through the same process
        #             and then measure the noise from that image.  Use it to either whiten or
        #             symmetrize the noise in the final image.
        # Note: This method doesn't work any better.  The added noise for whitening is even more
        # than strategy 1.  And the remaining correlations are still similarly significant for the
        # symmetrize version.  A little smaller than strategy 1, but not enough to pass our tests.

        # Make another noise image, since we don't actually have access to a pure noise image
        # for real objects.  But we should be able to estimate the variance in the image.
        t3 = time.time()
        noise_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs)
        noise_image.addNoise(
            galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var)))
        noise_ii = galsim.InterpolatedImage(noise_image, pad_factor=1)
        sheared_noise_obj = galsim.Convolve(noise_ii, psf_inv).shear(shear)
        final_noise_obj = galsim.Convolve(psf_target, sheared_noise_obj)
        final_noise_image = final_noise_obj.drawImage(obs_image.copy(),
                                                      method='no_pixel')

        # Use this to construct an appropriate CorrelatedNoise object
        noise = galsim.CorrelatedNoise(final_noise_image)
        t4 = time.time()
        final_image2 = final_image.copy()
        final_var = final_image2.whitenNoise(noise)
        t5 = time.time()

        check_noise(
            final_noise_image, noise,
            'noise model is wrong when direct measuring the final noise image')

        print('Direct noise method with whiten: final_var = ', final_var)
        # Neither of these work currently, so maybe a bug in the whitening code?
        # Or possibly in my logic here.
        check_symm_noise(
            final_image2,
            'whitening the noise using direct noise model failed')
        print('Time for direct noise with whitening = ', t5 - t3)

        t6 = time.time()
        final_image2 = final_image.copy()
        final_var = final_image2.symmetrizeNoise(noise)
        t7 = time.time()

        print('Direct noise method with symmetrize: final_var = ', final_var)
        check_symm_noise(
            final_image2,
            'symmetrizing the noise using direct noise model failed')
        print('Time for direct noise with symmetrizing = ', t7 - t6 + t4 - t3)

    if False:
        print('\n\nStrategy 3:')
        # Strategy 3: Make a noise field and do the same operations as we do to the main image
        #             except use the opposite shear value.  Add this noise field to the final
        #             image to get a symmetric noise field.
        # Note: This method works!  But only for square pixels.  However, they may be rotated
        # or flipped. Just not sheared.
        # Update: I think this method won't ever work for non-square pixels.  The reason it works
        # for square pixels is that in that case, it is equivalent to strategy 4.
        t3 = time.time()

        # Make another noise image
        rev_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs)
        rev_image.addNoise(
            galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var)))
        rev_ii = galsim.InterpolatedImage(rev_image, pad_factor=1)

        rev_sheared_obj = galsim.Convolve(rev_ii, psf_inv).shear(-shear)
        rev_final_obj = galsim.Convolve(psf_target, rev_sheared_obj)
        rev_final_image = rev_final_obj.drawImage(obs_image.copy(),
                                                  method='no_pixel')

        # Add the reverse-sheared noise image to the original image.
        final_image2 = final_image + rev_final_image
        t4 = time.time()

        # The noise variance in the end should be 2x as large as the original
        final_var = np.var(final_image2.array)
        print('Reverse shear method: final_var = ', final_var)
        check_symm_noise(final_image2, 'using reverse shear does not work')
        print('Time for reverse shear method = ', t4 - t3)

    if True:
        print('\n\nStrategy 4:')
        # Strategy 4: Make a noise field and do the same operations as we do to the main image,
        #             then rotate it by 90 degress and add it to the final image.
        # This method works!  Even for an arbitrarily sheared wcs.
        t3 = time.time()

        # Make another noise image
        noise_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs)
        noise_image.addNoise(
            galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var)))
        noise_ii = galsim.InterpolatedImage(noise_image, pad_factor=1)

        noise_sheared_obj = galsim.Convolve(noise_ii, psf_inv).shear(shear)
        noise_final_obj = galsim.Convolve(psf_target, noise_sheared_obj)
        noise_final_image = noise_final_obj.drawImage(obs_image.copy(),
                                                      method='no_pixel')

        # Rotate the image by 90 degrees
        rot_noise_final_image = galsim.Image(
            np.ascontiguousarray(np.rot90(noise_final_image.array)))

        # Add the rotated noise image to the original image.
        final_image2 = final_image + rot_noise_final_image
        t4 = time.time()

        # The noise variance in the end should be 2x as large as the original
        final_var = np.var(final_image2.array)
        print('Rotate image method: final_var = ', final_var)
        check_symm_noise(final_image2,
                         'using rotated noise image does not work')
        print('Time for rotate image method = ', t4 - t3)

    if False:
        print('\n\nStrategy 5:')
        # Strategy 5: The same as strategy 3, except we target the effective net transformation
        #             done by strategy 4.
        # I think this strategy probably can't work for non-square pixels, because the shear
        # happens before the convolution by the PSF.  And if the wcs is non-square, then the
        # PSF is sheared relative to the pixels.  That shear isn't being accounted for here,
        # so the net result isn't equivalent to rotating by 90 degrees at the end.
        t3 = time.time()

        # Make another noise image
        rev_image = galsim.Image(im_size, im_size, init_value=0, wcs=wcs)
        rev_image.addNoise(
            galsim.GaussianNoise(rng=rng, sigma=math.sqrt(noise_var)))
        rev_ii = galsim.InterpolatedImage(rev_image, pad_factor=1)

        # Find the effective transformation to apply in sky coordinates that matches what
        # you would get by applying the shear in sky coords, going to image coords and then
        # rotating by 90 degrees.
        #
        # If J is the jacobian of the wcs, and S1 is the applied shear, then we want to find
        # S2 such that J^-1 S2 = R90 J^-1 S1
        jac = wcs.jacobian()
        J = jac.getMatrix()
        Jinv = jac.inverse().getMatrix()
        S1 = shear.getMatrix()
        R90 = np.array([[0, -1], [1, 0]])
        S2 = J.dot(R90).dot(Jinv).dot(S1)
        scale, rev_shear, rev_theta, flip = galsim.JacobianWCS(
            *S2.flatten()).getDecomposition()
        # Flip should be False, and scale should be essentially 1.0.
        assert flip == False
        assert abs(scale - 1.) < 1.e-8

        rev_sheared_obj = galsim.Convolve(
            rev_ii, psf_inv).rotate(rev_theta).shear(rev_shear)
        rev_final_obj = galsim.Convolve(psf_target, rev_sheared_obj)
        rev_final_image = rev_final_obj.drawImage(obs_image.copy(),
                                                  method='no_pixel')

        # Add the reverse-sheared noise image to the original image.
        final_image2 = final_image + rev_final_image
        t4 = time.time()

        # The noise variance in the end should be 2x as large as the original
        final_var = np.var(final_image2.array)
        print('Alternate reverse shear method: final_var = ', final_var)
        check_symm_noise(final_image2,
                         'using alternate reverse shear does not work')
        print('Time for alternate reverse shear method = ', t4 - t3)