def test_sum_noise(): """Test that noise propagation works properly for compound objects. """ obj1 = galsim.Gaussian(sigma=1.7) obj2 = galsim.Gaussian(sigma=2.3) obj1.noise = galsim.UncorrelatedNoise(variance=0.3, scale=0.2) obj2.noise = galsim.UncorrelatedNoise(variance=0.5, scale=0.2) obj3 = galsim.Gaussian(sigma=2.9) # Sum adds the variance of the components sum2 = galsim.Sum([obj1,obj2]) np.testing.assert_almost_equal(sum2.noise.getVariance(), 0.8, err_msg = "Sum of two objects did not add noise varinace") sum2 = galsim.Sum([obj1,obj3]) np.testing.assert_almost_equal(sum2.noise.getVariance(), 0.3, err_msg = "Sum of two objects did not add noise varinace") sum2 = galsim.Sum([obj2,obj3]) np.testing.assert_almost_equal(sum2.noise.getVariance(), 0.5, err_msg = "Sum of two objects did not add noise varinace") sum3 = galsim.Sum([obj1,obj2,obj3]) np.testing.assert_almost_equal(sum3.noise.getVariance(), 0.8, err_msg = "Sum of three objects did not add noise varinace") sum3 = obj1 + obj2 + obj3 np.testing.assert_almost_equal(sum3.noise.getVariance(), 0.8, err_msg = "Sum of three objects did not add noise varinace") # Adding noise objects with different WCSs will raise a warning. obj4 = galsim.Gaussian(sigma=3.3) obj4.noise = galsim.UncorrelatedNoise(variance=0.3, scale=0.8) try: np.testing.assert_warns(galsim.GalSimWarning, galsim.Sum, [obj1, obj4]) except: pass
def addNoise(self, config, base, im, rng, current_var, draw_method, logger): # Build the correlated noise cn = self.getCOSMOSNoise(config, base, rng) var = cn.getVariance() # Subtract off the current variance if any if current_var: logger.debug( 'image %d, obj %d: Target variance is %f, current variance is %f', base.get('image_num', 0), base.get('obj_num', 0), var, current_var) if var < current_var: raise galsim.GalSimConfigError( "Whitening already added more noise than the requested COSMOS noise." ) cn -= galsim.UncorrelatedNoise(current_var, rng=rng, wcs=cn.wcs) # Add the noise to the image im.addNoise(cn) logger.debug( 'image %d, obj %d: Added COSMOS correlated noise with variance = %f', base.get('image_num', 0), base.get('obj_num', 0), var) return var
def getNoise(self, i, rng=None, gsparams=None): """Returns the noise cf at index `i` as a CorrelatedNoise object. """ if self.noise_file_name is None: cf = galsim.UncorrelatedNoise(rng, self.pixel_scale[i], self.variance[i], gsparams) else: if i >= len(self.noise_file_name): raise IndexError( 'index %d given to getNoise is out of range (0..%d)'%( i,len(self.noise_file_name)-1)) if self.noise_file_name[i] in self.saved_noise_im: im = self.saved_noise_im[self.noise_file_name[i]] else: import pyfits import os import numpy file_name = os.path.join(self.noise_dir,self.noise_file_name[i]) array = pyfits.getdata(file_name) im = galsim.ImageViewD(numpy.ascontiguousarray(array.astype(numpy.float64))) self.saved_noise_im[self.noise_file_name[i]] = im cf = galsim.correlatednoise._BaseCorrelatedNoise( rng, galsim.InterpolatedImage(im, dx=self.pixel_scale[i], normalization="sb", calculate_stepk=False, calculate_maxk=False, x_interpolant='linear', gsparams=gsparams)) cf.setVariance(self.variance[i]) return cf
def getNoise(self, i, rng=None, gsparams=None): """Returns the noise correlation function at index `i` as a CorrelatedNoise object. Note: the return value from this function is not picklable, so this cannot be used across processes. """ im, scale, var = self.getNoiseProperties(i) if im is None: cf = galsim.UncorrelatedNoise(var, rng=rng, scale=scale, gsparams=gsparams) else: ii = galsim.InterpolatedImage(im, normalization="sb", calculate_stepk=False, calculate_maxk=False, x_interpolant='linear', gsparams=gsparams) cf = galsim.correlatednoise._BaseCorrelatedNoise(rng, ii, im.wcs) cf = cf.withVariance(var) return cf
def AddNoiseCOSMOS(noise, config, draw_method, rng, im, weight_im, current_var, sky, logger): # NB: Identical for fft and phot req = {'file_name': str} opt = {'cosmos_scale': float, 'variance': float} kwargs = galsim.config.GetAllParams(noise, 'noise', config, req=req, opt=opt, ignore=noise_ignore)[0] # Build the correlated noise cn = galsim.correlatednoise.getCOSMOSNoise(rng, **kwargs) var = cn.getVariance() # If we are saving the noise level in a weight image, do that now. if weight_im: weight_im += var # Subtract off the current variance if any if current_var: if var < current_var: raise RuntimeError( "Whitening already added more noise than requested COSMOS noise." ) cn -= galsim.UncorrelatedNoise(rng, im.wcs, current_var) # Add the noise to the image im.addNoise(cn) if logger: logger.debug( 'image %d, obj %d: Added COSMOS correlated noise with variance = %f', config['image_num'], config['obj_num'], var)
def test_convolve_noise(): """Test that noise propagation works properly for compount objects. """ obj1 = galsim.Gaussian(sigma=1.7) obj2 = galsim.Gaussian(sigma=2.3) obj1.noise = galsim.UncorrelatedNoise(variance=0.3, scale=0.2) obj2.noise = galsim.UncorrelatedNoise(variance=0.5, scale=0.2) obj3 = galsim.Gaussian(sigma=2.9) # Convolve convolves the noise from a single component conv2 = galsim.Convolution([obj1, obj3]) noise = galsim.Convolve([obj1.noise._profile, obj3, obj3]) # xValue is too slow here. Use drawImage to get variance. (Just as CorrelatedNoise does.) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1, 1) np.testing.assert_almost_equal( conv2.noise.getVariance(), variance, err_msg= "Convolution of two objects did not correctly propagate noise varinace" ) conv2 = galsim.Convolution([obj2, obj3]) noise = galsim.Convolve([obj2.noise._profile, obj3, obj3]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1, 1) np.testing.assert_almost_equal( conv2.noise.getVariance(), variance, err_msg= "Convolution of two objects did not correctly propagate noise varinace" ) # Convolution of multiple objects with noise attributes raises a warning and fails # to propagate noise properly. (It takes the input noise from the first one.) conv2 = galsim.Convolution(obj1, obj2) conv3 = galsim.Convolution(obj1, obj2, obj3) with assert_warns(galsim.GalSimWarning): assert conv2.noise == obj1.noise.convolvedWith(obj2) with assert_warns(galsim.GalSimWarning): assert conv3.noise == obj1.noise.convolvedWith( galsim.Convolve(obj2, obj3)) # Convolution with only one object uses that object's noise conv1 = galsim.Convolution(obj1) assert conv1.noise == obj1.noise # Other types don't propagate noise and give a warning about it. deconv = galsim.Deconvolution(obj2) autoconv = galsim.AutoConvolution(obj2) autocorr = galsim.AutoCorrelation(obj2) four = galsim.FourierSqrt(obj2) with assert_warns(galsim.GalSimWarning): assert deconv.noise is None with assert_warns(galsim.GalSimWarning): assert autoconv.noise is None with assert_warns(galsim.GalSimWarning): assert autocorr.noise is None with assert_warns(galsim.GalSimWarning): assert four.noise is None obj2.noise = None # Remove obj2 noise for the rest. conv2 = galsim.Convolution(obj1, obj2) noise = galsim.Convolve([obj1.noise._profile, obj2, obj2]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1, 1) np.testing.assert_almost_equal( conv2.noise.getVariance(), variance, err_msg= "Convolution of two objects did not correctly propagate noise varinace" ) conv3 = galsim.Convolution(obj1, obj2, obj3) noise = galsim.Convolve([obj1.noise._profile, obj2, obj2, obj3, obj3]) variance = noise.drawImage(nx=1, ny=1, scale=1., method='sb')(1, 1) np.testing.assert_almost_equal( conv3.noise.getVariance(), variance, err_msg= "Convolution of three objects did not correctly propagate noise varinace" ) deconv = galsim.Deconvolution(obj2) autoconv = galsim.AutoConvolution(obj2) autocorr = galsim.AutoCorrelation(obj2) four = galsim.FourierSqrt(obj2) assert deconv.noise is None assert autoconv.noise is None assert autocorr.noise is None assert four.noise is None
def test_crg_roundtrip_larger_target_psf(): """Test that drawing a chromatic galaxy with a color gradient directly using an LSST-size PSF is equivalent to first drawing the galaxy to HST-like images, and then using ChromaticRealGalaxy to produce an LSST-like image. """ # load some spectra bulge_SED = (galsim.SED( os.path.join(sedpath, 'CWW_E_ext.sed'), wave_type='ang', flux_type='flambda').thin(rel_err=1e-3).withFluxDensity( target_flux_density=0.3, wavelength=500.0)) disk_SED = (galsim.SED( os.path.join(sedpath, 'CWW_Sbc_ext.sed'), wave_type='ang', flux_type='flambda').thin(rel_err=1e-3).withFluxDensity( target_flux_density=0.3, wavelength=500.0)) bulge = galsim.Sersic(n=4, half_light_radius=0.6) * bulge_SED disk = galsim.Sersic(n=1, half_light_radius=0.4) * disk_SED # Decenter components a bit to make the test more complicated disk = disk.shift(0.05, 0.1) gal = (bulge + disk).shear(g1=0.3, g2=0.1) # Much faster to just use some achromatic HST-like PSFs. We'll make them slightly different in # each band though. f606w_PSF = galsim.ChromaticObject(galsim.Gaussian(half_light_radius=0.05)) f814w_PSF = galsim.ChromaticObject(galsim.Gaussian(half_light_radius=0.07)) LSSTPSF = galsim.ChromaticAtmosphere(galsim.Kolmogorov(fwhm=0.7), 600.0, zenith_angle=0.0 * galsim.degrees) f606w = galsim.Bandpass(os.path.join(bppath, "ACS_wfc_F606W.dat"), 'nm').truncate() f814w = galsim.Bandpass(os.path.join(bppath, "ACS_wfc_F814W.dat"), 'nm') LSST_i = galsim.Bandpass(os.path.join(bppath, "LSST_r.dat"), 'nm') truth_image = galsim.Convolve(LSSTPSF, gal).drawImage(LSST_i, nx=24, ny=24, scale=0.2) f606w_image = galsim.Convolve(f606w_PSF, gal).drawImage(f606w, nx=192, ny=192, scale=0.03) f814w_image = galsim.Convolve(f814w_PSF, gal).drawImage(f814w, nx=192, ny=192, scale=0.03) crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) test_image = galsim.Convolve(crg, LSSTPSF).drawImage(LSST_i, nx=24, ny=24, scale=0.2) truth_mom = galsim.hsm.FindAdaptiveMom(truth_image) test_mom = galsim.hsm.FindAdaptiveMom(test_image) np.testing.assert_allclose(test_mom.moments_amp, truth_mom.moments_amp, rtol=1e-3, atol=0) np.testing.assert_allclose(test_mom.moments_centroid.x, truth_mom.moments_centroid.x, rtol=0., atol=1e-2) np.testing.assert_allclose(test_mom.moments_centroid.y, truth_mom.moments_centroid.y, rtol=0., atol=1e-2) np.testing.assert_allclose(test_mom.moments_sigma, truth_mom.moments_sigma, rtol=1e-3, atol=0) np.testing.assert_allclose(test_mom.observed_shape.g1, truth_mom.observed_shape.g1, rtol=0, atol=1e-4) np.testing.assert_allclose(test_mom.observed_shape.g2, truth_mom.observed_shape.g2, rtol=0, atol=1e-4) # Invalid arguments with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)], SEDs=[bulge_SED, disk_SED]) with assert_raises(ValueError): crg = galsim.ChromaticRealGalaxy.makeFromImages( images=[f606w_image, f814w_image], bands=[f606w, f814w], PSFs=[f606w_PSF, f814w_PSF], xis=[galsim.UncorrelatedNoise(1e-16)] * 2, SEDs=[bulge_SED, disk_SED, disk_SED])
def test_whiten(): """Test the options in config to whiten images """ real_gal_dir = os.path.join('..','examples','data') real_gal_cat = 'real_galaxy_catalog_23.5_example.fits' config = { 'image' : { 'type' : 'Single', 'random_seed' : 1234, 'pixel_scale' : 0.05, }, 'stamp' : { 'type' : 'Basic', 'size' : 32, }, 'gal' : { 'type' : 'RealGalaxy', 'index' : 79, 'flux' : 100, }, 'psf' : { # This is really slow if we don't convolve by a PSF. 'type' : 'Gaussian', 'sigma' : 0.05 }, 'input' : { 'real_catalog' : { 'dir' : real_gal_dir , 'file_name' : real_gal_cat } } } # First build by hand (no whitening yet) rng = galsim.BaseDeviate(1234 + 1) rgc = galsim.RealGalaxyCatalog(os.path.join(real_gal_dir, real_gal_cat)) gal = galsim.RealGalaxy(rgc, index=79, flux=100, rng=rng) psf = galsim.Gaussian(sigma=0.05) final = galsim.Convolve(gal,psf) im1a = final.drawImage(nx=32, ny=32, scale=0.05) # Compare to what config builds galsim.config.ProcessInput(config) im1b, cv1b = galsim.config.BuildStamp(config, do_noise=False) np.testing.assert_equal(cv1b, 0.) np.testing.assert_equal(im1b.array, im1a.array) # Now add whitening, but no noise yet. cv1a = final.noise.whitenImage(im1a) print('From whiten, current_var = ',cv1a) galsim.config.RemoveCurrent(config) config['image']['noise'] = { 'whiten' : True, } im1c, cv1c = galsim.config.BuildStamp(config, do_noise=False) print('From BuildStamp, current_var = ',cv1c) np.testing.assert_equal(cv1c, cv1a) np.testing.assert_equal(im1c.array, im1a.array) rng1 = rng.duplicate() # Save current state of rng # 1. Gaussian noise ##### config['image']['noise'] = { 'type' : 'Gaussian', 'variance' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im2a = im1a.copy() im2a.addNoise(galsim.GaussianNoise(sigma=math.sqrt(50-cv1a), rng=rng)) im2b, cv2b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv2b, 50) np.testing.assert_almost_equal(im2b.array, im2a.array, decimal=5) # If whitening already added too much noise, raise an exception config['image']['noise']['variance'] = 1.e-5 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 2. Poisson noise ##### config['image']['noise'] = { 'type' : 'Poisson', 'sky_level_pixel' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im3a = im1a.copy() sky = 50 - cv1a rng.reset(rng1.duplicate()) im3a.addNoise(galsim.PoissonNoise(sky_level=sky, rng=rng)) im3b, cv3b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv3b, 50, decimal=5) np.testing.assert_almost_equal(im3b.array, im3a.array, decimal=5) # It's more complicated if the sky is quoted per arcsec and the wcs is not uniform. config2 = galsim.config.CopyConfig(config) galsim.config.RemoveCurrent(config2) config2['image']['sky_level'] = 100 config2['image']['wcs'] = { 'type' : 'UVFunction', 'ufunc' : '0.05*x + 0.001*x**2', 'vfunc' : '0.05*y + 0.001*y**2', } del config2['image']['pixel_scale'] del config2['wcs'] config2['image']['noise']['symmetrize'] = 4 # Also switch to symmetrize, just to mix it up. del config2['image']['noise']['whiten'] rng.reset(1234+1) # Start fresh, since redoing the whitening/symmetrizing wcs = galsim.UVFunction(ufunc='0.05*x + 0.001*x**2', vfunc='0.05*y + 0.001*y**2') im3c = galsim.Image(32,32, wcs=wcs) im3c = final.drawImage(im3c) cv3c = final.noise.symmetrizeImage(im3c,4) sky = galsim.Image(im3c.bounds, wcs=wcs) wcs.makeSkyImage(sky, 100) mean_sky = np.mean(sky.array) im3c += sky extra_sky = 50 - cv3c im3c.addNoise(galsim.PoissonNoise(sky_level=extra_sky, rng=rng)) im3d, cv3d = galsim.config.BuildStamp(config2) np.testing.assert_almost_equal(cv3d, 50 + mean_sky, decimal=4) np.testing.assert_almost_equal(im3d.array, im3c.array, decimal=5) config['image']['noise']['sky_level_pixel'] = 1.e-5 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 3. CCDNoise ##### config['image']['noise'] = { 'type' : 'CCD', 'sky_level_pixel' : 25, 'read_noise' : 5, 'gain' : 1, 'whiten' : True, } galsim.config.RemoveCurrent(config) im4a = im1a.copy() rn = math.sqrt(25-cv1a) rng.reset(rng1.duplicate()) im4a.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=1, rng=rng)) im4b, cv4b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv4b, 50, decimal=5) np.testing.assert_almost_equal(im4b.array, im4a.array, decimal=5) # Repeat with gain != 1 config['image']['noise']['gain'] = 3.7 galsim.config.RemoveCurrent(config) im5a = im1a.copy() rn = math.sqrt(25-cv1a * 3.7**2) rng.reset(rng1.duplicate()) im5a.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=3.7, rng=rng)) im5b, cv5b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv5b, 50, decimal=5) np.testing.assert_almost_equal(im5b.array, im5a.array, decimal=5) # And again with a non-trivial sky image galsim.config.RemoveCurrent(config2) config2['image']['noise'] = config['image']['noise'] config2['image']['noise']['symmetrize'] = 4 del config2['image']['noise']['whiten'] rng.reset(1234+1) im5c = galsim.Image(32,32, wcs=wcs) im5c = final.drawImage(im5c) cv5c = final.noise.symmetrizeImage(im5c, 4) sky = galsim.Image(im5c.bounds, wcs=wcs) wcs.makeSkyImage(sky, 100) mean_sky = np.mean(sky.array) im5c += sky rn = math.sqrt(25-cv5c * 3.7**2) im5c.addNoise(galsim.CCDNoise(sky_level=25, read_noise=rn, gain=3.7, rng=rng)) im5d, cv5d = galsim.config.BuildStamp(config2) np.testing.assert_almost_equal(cv5d, 50 + mean_sky, decimal=4) np.testing.assert_almost_equal(im5d.array, im5c.array, decimal=5) config['image']['noise']['sky_level_pixel'] = 1.e-5 config['image']['noise']['read_noise'] = 0 try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass # 4. COSMOSNoise ##### file_name = os.path.join(galsim.meta_data.share_dir,'acs_I_unrot_sci_20_cf.fits') config['image']['noise'] = { 'type' : 'COSMOS', 'file_name' : file_name, 'variance' : 50, 'whiten' : True, } galsim.config.RemoveCurrent(config) im6a = im1a.copy() rng.reset(rng1.duplicate()) noise = galsim.getCOSMOSNoise(file_name=file_name, variance=50, rng=rng) noise -= galsim.UncorrelatedNoise(cv1a, rng=rng, wcs=noise.wcs) im6a.addNoise(noise) im6b, cv6b = galsim.config.BuildStamp(config) np.testing.assert_almost_equal(cv6b, 50, decimal=5) np.testing.assert_almost_equal(im6b.array, im6a.array, decimal=5) config['image']['noise']['variance'] = 1.e-5 del config['_current_cn_tag'] try: np.testing.assert_raises(RuntimeError, galsim.config.BuildStamp,config) except ImportError: pass
def test_cosmosnoise(): """Test that the config layer COSMOS noise works with keywords. """ import logging logger = None pix_scale = 0.03 random_seed = 123 # First make the image using COSMOSNoise without kwargs. config = {} # Either gal or psf is required, but it can be type = None, which means don't draw anything. config['gal'] = { 'type' : 'None' } config['stamp'] = { 'type' : 'Basic', 'size' : 64 } config['image'] = { 'pixel_scale' : pix_scale, 'random_seed' : 123 # Note: this means the seed for the noise will really be 124 # since it is applied at the stamp level, so uses seed + obj_num } config['image']['noise'] = { 'type' : 'COSMOS' } image = galsim.config.BuildStamp(config,logger=logger)[0] # Then make it using explicit kwargs to make sure they are getting passed through properly. config2 = {} config2['gal'] = config['gal'] config2['stamp'] = { 'type' : 'Basic', 'xsize' : 64, # Same thing, but cover the xsize, ysize options 'ysize' : 64 } config2['image'] = config['image'] config2['image']['noise'] = { 'type' : 'COSMOS', 'file_name' : os.path.join(galsim.meta_data.share_dir,'acs_I_unrot_sci_20_cf.fits'), 'cosmos_scale' : pix_scale } image2 = galsim.config.BuildStamp(config2,logger=logger)[0] # We used the same RNG and noise file / properties, so should get the same exact noise field. np.testing.assert_allclose( image.array, image2.array, rtol=1.e-5, err_msg='Config COSMOS noise does not reproduce results given kwargs') # Use a RealGalaxy with whitening to make sure that it properly handles any current_var # in the image already. # Detects bug Rachel found in issue #792 config['gal'] = { 'type' : 'RealGalaxy', 'index' : 79, # Use a small flux to make sure that whitening doesn't add more noise than we will # request from the COSMOS noise. (flux = 0.1 is too high.) 'flux' : 0.01 } real_gal_dir = os.path.join('..','examples','data') real_gal_cat = 'real_galaxy_catalog_23.5_example.fits' config['input'] = { 'real_catalog' : { 'dir' : real_gal_dir , 'file_name' : real_gal_cat } } config['image']['noise']['whiten'] = True galsim.config.ProcessInput(config) image3, current_var3 = galsim.config.BuildStamp(config, logger=logger) print('From BuildStamp, current_var = ',current_var3) # Build the same image by hand to make sure it matches what config drew. rng = galsim.BaseDeviate(124) rgc = galsim.RealGalaxyCatalog(os.path.join(real_gal_dir, real_gal_cat)) gal = galsim.RealGalaxy(rgc, index=79, flux=0.01, rng=rng) image4 = gal.drawImage(image=image3.copy()) current_var4 = gal.noise.whitenImage(image4) print('After whitening, current_var = ',current_var4) noise = galsim.correlatednoise.getCOSMOSNoise( rng=rng, file_name=os.path.join(galsim.meta_data.share_dir,'acs_I_unrot_sci_20_cf.fits'), cosmos_scale=pix_scale) # Check that the COSMOSNoiseBuilder calculates the right variance. var1 = noise.getVariance() var2 = galsim.config.CalculateNoiseVariance(config) print('Full noise variance = ',noise.getVariance()) print('From config.CalculateNoiseVar = ',var2) np.testing.assert_almost_equal(var2, var1, err_msg="COSMOSNoise calculated the wrong variance") # Finish whitening steps. np.testing.assert_equal( current_var3, noise.getVariance(), err_msg='Config COSMOS noise with whitening does not return the correct current_var') noise -= galsim.UncorrelatedNoise(current_var4, rng=rng, wcs=image4.wcs) print('After subtract current_var, noise variance = ',noise.getVariance()) image4.addNoise(noise) np.testing.assert_equal( image3.array, image4.array, err_msg='Config COSMOS noise with whitening does not reproduce manually drawn image') # If CalculateNoiseVar happens before using the noise, there is a slightly different code # path, but it should return the same answer of course. del config['_current_cn_tag'] var3 = galsim.config.CalculateNoiseVariance(config) print('From config.CalculateNoiseVar = ',var3) np.testing.assert_almost_equal(var3, var1, err_msg="COSMOSNoise calculated the wrong variance") # AddNoiseVariance should add the variance to an image image5 = galsim.Image(32,32) galsim.config.AddNoiseVariance(config, image5) np.testing.assert_almost_equal(image5.array, var1, err_msg="AddNoiseVariance added the wrong variance")
def test_dep_correlatednoise(): """Test the deprecated methods in galsim/deprecated/correlatednoise.py """ rng = galsim.BaseDeviate(123) n1 = galsim.UncorrelatedNoise(variance=0.01, scale=1.3, rng=rng.duplicate()) n2 = galsim.UncorrelatedNoise(variance=0.01, scale=1.3, rng=rng.duplicate()) b = galsim.BoundsI(1, 3, 1, 3) mat = check_dep(n1.calculateCovarianceMatrix, bounds=b, scale=1.9) # No replacement, so nothing to compare with. check_dep(n1.setVariance, variance=1.7) np.testing.assert_almost_equal(n1.getVariance(), 1.7) check_dep(n1.scaleVariance, variance_ratio=1.9) np.testing.assert_almost_equal(n1.getVariance(), 1.7 * 1.9) n2 = n2.withVariance(1.7 * 1.9) n2 = n2.expand(4.3) n3 = check_dep(n1.createExpanded, scale=4.3) gsobject_compare(n3, n2) check_dep(n1.applyExpansion, scale=4.3) gsobject_compare(n1, n2) n2 = n2.shear(g1=0.3, g2=-0.13) n3 = check_dep(n1.createSheared, g1=0.3, g2=-0.13) gsobject_compare(n3, n2) check_dep(n1.applyShear, g1=0.3, g2=-0.13) gsobject_compare(n1, n2) n2 = n2.dilate(0.54) n3 = check_dep(n1.createDilated, scale=0.54) gsobject_compare(n3, n2) check_dep(n1.applyDilation, scale=0.54) gsobject_compare(n1, n2) n2 = n2.magnify(1.1) n3 = check_dep(n1.createMagnified, mu=1.1) gsobject_compare(n3, n2) check_dep(n1.applyMagnification, mu=1.1) gsobject_compare(n1, n2) n2 = n2.lens(g1=0.31, g2=0.48, mu=0.22) n3 = check_dep(n1.createLensed, g1=0.31, g2=0.48, mu=0.22) gsobject_compare(n3, n2) check_dep(n1.applyLensing, g1=0.31, g2=0.48, mu=0.22) gsobject_compare(n1, n2) n2 = n2.rotate(38.4 * galsim.degrees) n3 = check_dep(n1.createRotated, 38.4 * galsim.degrees) gsobject_compare(n3, n2) check_dep(n1.applyRotation, theta=38.4 * galsim.degrees) gsobject_compare(n1, n2) n2 = n2.transform(0.1, 1.09, -1.32, -0.09) n3 = check_dep(n1.createTransformed, dudx=0.1, dudy=1.09, dvdx=-1.32, dvdy=-0.09) gsobject_compare(n3, n2) check_dep(n1.applyTransformation, dudx=0.1, dudy=1.09, dvdx=-1.32, dvdy=-0.09) gsobject_compare(n1, n2) g = galsim.Gaussian(sigma=0.34) n2 = n2.convolvedWith(g) check_dep(n1.convolveWith, g) gsobject_compare(n1, n2) im1 = n1.drawImage() im2 = check_dep(n2.draw) np.testing.assert_almost_equal(im1.scale, im2.scale) np.testing.assert_equal(im1.bounds, im2.bounds) np.testing.assert_array_almost_equal(im1.array, im2.array) n1.whitenImage(im1) check_dep(n2.applyWhiteningTo, im2) np.testing.assert_array_almost_equal(im1.array, im2.array)
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 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)
def test_cosmosnoise(): """Test that the config layer COSMOS noise works with keywords. """ import logging logger = None pix_scale = 0.03 random_seed = 123 # First make the image using COSMOSNoise without kwargs. config = {} # Either gal or psf is required, so just give it a Gaussian with 0 flux. config['gal'] = {'type': 'Gaussian', 'sigma': 0.1, 'flux': 0} config['image'] = { 'type': 'Single', 'pixel_scale': pix_scale, 'random_seed': 123 # Note: this means the seed for the noise will really be 124 # since it is applied at the stamp level, so uses seed + obj_num } config['image']['noise'] = {'type': 'COSMOS'} image = galsim.config.BuildImage(config, logger=logger) # Then make using kwargs explicitly, to make sure they are getting passed through properly. config2 = {} # Either gal or psf is required, so just give it a Gaussian with 0 flux. config2['gal'] = config['gal'] config2['image'] = config['image'] config2['image']['noise'] = { 'type': 'COSMOS', 'file_name': os.path.join(galsim.meta_data.share_dir, 'acs_I_unrot_sci_20_cf.fits'), 'cosmos_scale': pix_scale } image2 = galsim.config.BuildImage(config2, logger=logger) # We used the same RNG and noise file / properties, so should get the same exact noise field. np.testing.assert_allclose( image.array, image2.array, rtol=1.e-5, err_msg='Config COSMOS noise does not reproduce results given kwargs') # Use a RealGalaxy with whitening to make sure that it properly handles any current_var # in the image already. # Detects bug Rachel found in issue #792 config['gal'] = { 'type': 'RealGalaxy', 'index': 79, # Use a small flux to make sure that whitening doesn't add more noise than we will # request from the COSMOS noise. (flux = 0.1 is too high.) 'flux': 0.01 } real_gal_dir = os.path.join('..', 'examples', 'data') real_gal_cat = 'real_galaxy_catalog_23.5_example.fits' config['input'] = { 'real_catalog': { 'dir': real_gal_dir, 'file_name': real_gal_cat } } config['image']['noise']['whiten'] = True galsim.config.ProcessInput(config) image3 = galsim.config.BuildImage(config, logger=logger) # Build the same image by hand to make sure it matches what config drew. rng = galsim.BaseDeviate(124) rgc = galsim.RealGalaxyCatalog(os.path.join(real_gal_dir, real_gal_cat)) gal = galsim.RealGalaxy(rgc, index=79, flux=0.01, rng=rng) image4 = gal.drawImage(image=image3.copy()) current_var = gal.noise.whitenImage(image4) noise = galsim.correlatednoise.getCOSMOSNoise( rng=rng, file_name=os.path.join(galsim.meta_data.share_dir, 'acs_I_unrot_sci_20_cf.fits'), cosmos_scale=pix_scale) noise -= galsim.UncorrelatedNoise(current_var, rng=rng, wcs=image4.wcs) image4.addNoise(noise) image3.write('image3.fits') image4.write('image4.fits') np.testing.assert_equal( image3.array, image4.array, err_msg= 'Config COSMOS noise with whiting does not reproduce manually drawn image' )
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)