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
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))
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)
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)
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])
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
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)
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")
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
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
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)
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, )
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
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)
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')
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)
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(
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)
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
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)
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
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
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)