def test_psf_gen_psf_wldeblend_ps_rng(): rng = np.random.RandomState(seed=42) psf_config = { "type": "wldeblend-ps", "variation_factor": 8, } gs_config, psf = gen_psf( rng=rng, psf_config=psf_config, gal_config={"type": "lsst-riz"}, se_image_shape=100, se_wcs=galsim.PixelScale(0.2), ) gmod = psf.getPSF(galsim.PositionD(x=10, y=10)) rng = np.random.RandomState(seed=42) psf_config = { "type": "wldeblend-ps", "variation_factor": 8, } gs_config1, psf1 = gen_psf( rng=rng, psf_config=psf_config, gal_config={"type": "lsst-riz"}, se_image_shape=100, se_wcs=galsim.PixelScale(0.2), ) gmod1 = psf1.getPSF(galsim.PositionD(x=10, y=10)) assert gmod1 == gmod assert gs_config1 == gs_config assert gs_config["beta"] == 2.5 assert gs_config["type"] == "Moffat" assert psf._variation_factor == 8 assert psf._median_seeing == 0.85
def _set_target_psf(self): # find the largets kernel according to # sqrt(T) * dilation # where # g = sqrt(shear.g1**2 + shear.g2**2) # dilation = 1.0 + 2.0*g self._target_psf_image = None self._target_psf_size = None self._psf_im_shape = None ni = self.image_shape[0] // self.patch_size nj = self.image_shape[1] // self.patch_size dg = (self.patch_size - 1) / 2 for i in range(ni + 1): row = min(i * self.patch_size + dg, self.image_shape[0]) for j in range(nj + 1): col = min(j * self.patch_size + dg, self.image_shape[1]) psf_im = self.psf_model(row, col) psf_im /= np.sum(psf_im) if self._psf_im_shape is None: self._psf_im_shape = psf_im.shape assert self._psf_im_shape[0] % 2 == 1 assert self._psf_im_shape[1] % 2 == 1 assert self._psf_im_shape[0] == self._psf_im_shape[1] else: assert self._psf_im_shape == psf_im.shape hsmpars = galsim.hsm.HSMParams(max_mom2_iter=1000) gim = galsim.ImageD(psf_im, wcs=galsim.PixelScale(1)) try: moms = galsim.hsm.FindAdaptiveMom(gim, hsmparams=hsmpars) fac = gim.calculateFWHM() * (1.0 + 2.0 * np.sqrt( moms.observed_shape.g1**2 + moms.observed_shape.g2**2)) if (self._target_psf_size is None or fac > self._target_psf_size): self._target_psf_size = fac self._target_psf_image = psf_im self._target_psf_loc = (row, col) except galsim.errors.GalSimHSMError: pass # the final PSF is an interpolated image convolved with the Gaussian # smoothing kernel self._target_psf = galsim.Convolve( galsim.InterpolatedImage(galsim.ImageD(self._target_psf_image), wcs=galsim.PixelScale(1)), galsim.Gaussian(sigma=self.sigma))
def test_render_source_in_image(x, y): source = galsim.Gaussian(fwhm=0.9) local_wcs = galsim.PixelScale(0.25) draw_method = 'auto' image_pos = galsim.PositionD(x=x, y=y) expected_shape = source.drawImage(wcs=local_wcs, method=draw_method).array.shape stamp = render_source_in_image(source=source, image_pos=image_pos, local_wcs=local_wcs, draw_method=draw_method) assert stamp.array.shape == expected_shape # any odd number works here for the image size - need to have image center # be a pixel center full_img = source.shift(dx=image_pos.x * 0.25, dy=image_pos.y * 0.25).drawImage(wcs=local_wcs, nx=187, ny=187) # since the shift method offsets from image center, we set # image center to (0, 0) so the object shifts match the expected pixel # location full_img.setCenter(0, 0) # now the bounds of the stamp are in the same coords as the full image # so just grab that subset and compare assert np.allclose(full_img[stamp.bounds].array, stamp.array, rtol=0, atol=5e-7)
def getPSF(self, image_pos, pixel_scale=None, gsparams=None): """Returns the PSF at position image_pos @param image_pos The position in image coordinates at which to build the PSF. @param gsparams (Optional) A GSParams instance to pass to the constructed GSObject. @param pixel_scale A deprecated parameter that is only present for backwards compatibility. If the constructor did not provide an image file or wcs, then this will use the pixel scale for an approximate wcs. @returns the PSF as a GSObject """ # Build an image version of the numpy array im = galsim.Image(self.getPSFArray(image_pos)) # Build the PSF profile in the image coordinate system. psf = galsim.InterpolatedImage(im, scale=self.sample_scale, flux=1, x_interpolant=galsim.Lanczos(3), gsparams=gsparams) # This brings if from image coordinates to world coordinates. if self.wcs: psf = self.wcs.toWorld(psf, image_pos=image_pos) elif pixel_scale: depr( 'pixel_scale', 1.1, 'wcs=PixelScale(pixel_scale) in the constructor for DES_PSFEx') psf = galsim.PixelScale(pixel_scale).toWorld(psf) return psf
def BuildWCS(config): """Read the wcs parameters from the config dict and return a constructed wcs object. """ image = config['image'] # If there is a wcs field, read it and update the wcs variable. if 'wcs' in image: image_wcs = image['wcs'] if 'type' in image_wcs: wcs_type = image_wcs['type'] else: wcs_type = 'PixelScale' # Special case: origin == center means to use image_center for the wcs origin if 'origin' in image_wcs and image_wcs['origin'] == 'center': origin = config['image_center'] image_wcs['origin'] = origin if wcs_type not in valid_wcs_types: raise AttributeError("Invalid image.wcs.type=%s." % wcs_type) builder = valid_wcs_types[wcs_type] wcs = builder.buildWCS(image_wcs, config) else: # Default if no wcs is to use PixelScale if 'pixel_scale' in image: scale = galsim.config.ParseValue(image, 'pixel_scale', config, float)[0] else: scale = 1.0 wcs = galsim.PixelScale(scale) return wcs
def getKwargs(self, config, base, logger): req = {'file_name': str} opt = {'dir': str, 'image_file_name': str} kwargs, safe = galsim.config.GetAllParams(config, base, req=req, opt=opt) if 'image_file_name' not in kwargs: if 'wcs' in base: wcs = base['wcs'] if wcs.isLocal(): # Then the wcs is already fine. pass elif 'image_pos' in base: image_pos = base['image_pos'] wcs = wcs.local(image_pos) safe = False else: raise RuntimeError( "No image_pos found in config, but wcs is not local.") kwargs['wcs'] = wcs else: # Then we aren't doing normal config processing, so just use pixel scale = 1. kwargs['wcs'] = galsim.PixelScale(1.) return kwargs, safe
def test_render_sources_for_image(): image_shape = (64, 64) wcs = galsim.PixelScale(0.25) draw_method = 'auto' src_inds = np.arange(5) def src_func(ind): obj = galsim.Gaussian(fwhm=ind * 0.1 + 0.9) rng = np.random.RandomState(seed=ind + 1) image_pos = galsim.PositionD( x=rng.uniform(low=20, high=40), y=rng.uniform(low=20, high=40), ) return obj, image_pos im = render_sources_for_image(image_shape=image_shape, wcs=wcs, draw_method=draw_method, src_inds=src_inds, src_func=src_func, n_jobs=1) # now render them via direct sum of objects objs = [] for ind in src_inds: obj, pos = src_func(ind) objs.append(obj.shift(dx=pos.x * 0.25, dy=pos.y * 0.25)) im_sum = galsim.Sum(objs).drawImage(wcs=wcs, nx=155, ny=155) # we set the center to (0, 0) so that the offsets from the center # match the coords of the objects im_sum.setCenter(0, 0) assert np.allclose(im_sum[im.bounds].array, im.array, rtol=0, atol=5e-7)
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')
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 test_render_sim_smoke(): img_dim = 103 img_cen = (img_dim - 1)/2 scale = 0.25 method = 'auto' g1 = 0.0 g2 = 0.0 shear_scene = False objs = [galsim.Exponential(half_light_radius=0.5)] def _psf_function(*, x, y): assert np.allclose(x, img_cen) assert np.allclose(y, img_cen) return galsim.Gaussian(fwhm=0.9) uv_offsets = [galsim.PositionD(x=0.0, y=0.0)] uv_cen = galsim.PositionD(x=img_cen * scale, y=img_cen * scale) wcs = galsim.PixelScale(scale) se_img = render_objs_with_psf_shear( objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets, uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method, g1=g1, g2=g2, shear_scene=shear_scene) expected_img = galsim.Convolve( objs[0], galsim.Gaussian(fwhm=0.9) ).drawImage( nx=img_dim, ny=img_dim, scale=scale) assert np.allclose(expected_img.array, se_img.array, rtol=0, atol=1e-6)
def test_mask_and_apodize_stars(): rng = np.random.RandomState(seed=11) stars = np.array([(75, 85, 20)], dtype=[("x", "f8"), ("y", "f8"), ("radius_pixels", "f8")]) dim = 215 cdata = { "image": rng.normal(size=(dim, dim)), "weight": np.ones((dim, dim)), "mfrac": np.zeros((dim, dim)), "ormask": np.zeros((dim, dim), dtype=np.int32), "bmask": np.zeros((dim, dim), dtype=np.int32), "noise": rng.normal(size=(dim, dim)), "psf": rng.normal(size=(dim, dim)), } mbobs = make_mbobs_from_coadd_data( wcs=galsim.PixelScale(0.263).jacobian(), cdata=cdata, ) mbobs_old = copy.deepcopy(mbobs) rng = np.random.RandomState(seed=10) mask_stars( rng=rng, mbobs=mbobs, stars=stars, interp_cfg={ "skip": True, "iso_buff": 1, "fill_isolated_with_noise": False }, apodize_cfg={ "skip": False, "ap_rad": 1 }, mask_expand_rad=0, ) # check something changed for obslist, obslist_old in zip(mbobs, mbobs_old): for obs, obs_old in zip(obslist, obslist_old): for attr in ["image", "bmask", "noise", "weight", "mfrac"]: assert not np.array_equal(getattr(obs, attr), getattr(obs_old, attr)) # check some sentinel values assert np.all(mbobs[0][0].weight[80:80, 70:80] == 0) assert np.all(mbobs[0][0].mfrac[80:80, 70:80] == 1) assert np.all((mbobs[0][0].bmask[80:80, 70:80] & BMASK_GAIA_STAR) != 0) assert np.all((mbobs[0][0].bmask[80:80, 70:80] & BMASK_SPLINE_INTERP) == 0) # but not everything ofc assert not np.all(mbobs[0][0].weight == 0) assert not np.all(mbobs[0][0].mfrac == 1) assert not np.all((mbobs[0][0].bmask & BMASK_GAIA_STAR) != 0) assert np.all((mbobs[0][0].bmask & BMASK_SPLINE_INTERP) == 0)
def test_gauss_pix_psf_reproducible(): wcs = galsim.PixelScale(0.5) psf_model = GaussPixPSF(s2n=100) psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs) psf_im1 = psf.drawImage(nx=53, ny=53, scale=0.263).array psf_model = GaussPixPSF(s2n=100) psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs) psf_im2 = psf.drawImage(nx=53, ny=53, scale=0.263).array assert np.allclose(psf_im1, psf_im2)
def getKwargs(self, config, base, logger): req = { 'file_name' : str } opt = { 'dir' : str, 'image_file_name' : str } kwargs, safe = galsim.config.GetAllParams(config, base, req=req, opt=opt) if 'image_file_name' not in kwargs: if 'wcs' in base: kwargs['wcs'] = base['wcs'] else: # Then we aren't doing normal config processing, so just use pixel scale = 1. kwargs['wcs'] = galsim.PixelScale(1.) return kwargs, safe
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 make_objs( *, rng, blend, shear, top, hlr=1.0, flux=1e4, psf_fhwm=0.9, wcs_scale=0.2, npix=251, noise=1, ): if blend: if top: theta = np.random.uniform() * 2.0 * np.pi dx_pixels = 16 * np.cos(theta) dy_pixels = 16 * np.sin(theta) else: dx_pixels = 16 dy_pixels = 0 else: dx_pixels = 63 dy_pixels = 63 wcs = galsim.PixelScale(wcs_scale) psf = galsim.Gaussian(fwhm=psf_fhwm) dx = dx_pixels * wcs.scale dy = dy_pixels * wcs.scale dx1 = dx + rng.uniform(low=-0.5, high=0.5) * wcs.scale dy1 = dy + rng.uniform(low=-0.5, high=0.5) * wcs.scale dx2 = rng.uniform(low=-0.5, high=0.5) * wcs.scale dy2 = rng.uniform(low=-0.5, high=0.5) * wcs.scale cen = (npix - 1) / 2 cen1 = (cen - dx1 / wcs.scale, cen - dy1 / wcs.scale) cen2 = (cen - dx2 / wcs.scale, cen - dy2 / wcs.scale) obj1 = galsim.Exponential(half_light_radius=hlr).shear( g1=shear, g2=0).shift(dx=-dx1, dy=-dy1).withFlux(flux) obj2 = galsim.Exponential(half_light_radius=hlr).shift( dx=-dx2, dy=-dy2).withFlux(flux) im = galsim.Convolve(obj1 + obj2, psf).drawImage(wcs=wcs, nx=npix, ny=npix).array im += rng.normal(size=im.shape) * noise return im, psf, wcs, cen1, cen2, obj1, obj2, noise
def test_gauss_pix_psf_s2n(): wcs = galsim.PixelScale(0.263) psf_model = GaussPixPSF(s2n=100) psf = psf_model.getPSF(galsim.PositionD(x=1, y=2), wcs) psf_im = psf.drawImage(nx=53, ny=53, scale=0.263).array seed = 4098 rng = np.random.RandomState(seed=seed) g1 = rng.normal() * 0.01 g2 = rng.normal() * 0.01 fwhm = (rng.uniform(low=-0.1, high=0.1) + 1.0) * 0.9 gs = galsim.Gaussian(fwhm=fwhm).shear(g1=g1, g2=g2).withFlux(1.0) test_im = gs.drawImage(nx=53, ny=53, scale=0.263).array noise_std = np.sqrt(np.sum(test_im**2) / 100**2) nse = np.std(psf_im - test_im) assert nse < noise_std assert nse > 0
def ParseWorldPos(config, param_name, base, logger): """A helper function to parse the 'world_pos' value. The world_pos can be specified either as a regular RA, Dec (which in GalSim is known as a CelestialCoord) or as Euclidean coordinates in the local tangent plane relative to the image center (a PositionD). 1. For the RA/Dec option, the world_pos field should use the type RADec, which includes two values named ra and dec, each of which should be an Angle type. e.g.:: world_pos: type : RADec ra : 37 hours dec: -23 degrees Technically, any other type that results in a CelestialCoord is valid, but RADec is the only one that is defined natively in GalSim. 2. For the relative position in the local tangent plane (where 0,0 is the position of the image center), you can use any PositionD type. e.g.:: world_pos: type : RandomCircle radius : 12 # arcsec inner_radius : 3 # arcsec Parameters: config: The configuration dict for the stamp field. param_name: The name of the field in the config dict to parse as a world_pos. Normally, this is just 'world_pos'. base: The base configuration dict. Returns: either a CelestialCoord or a PositionD instance. """ param = config[param_name] wcs = base.get( 'wcs', galsim.PixelScale(1.0)) # should be here, but just in case... if wcs.isCelestial(): return galsim.config.ParseValue(config, param_name, base, galsim.CelestialCoord)[0] else: return galsim.config.ParseValue(config, param_name, base, galsim.PositionD)[0]
def test_render_sim_shear_scene(shear_scene): img_dim = 103 img_cen = (img_dim - 1)/2 scale = 0.25 method = 'auto' g1 = 0.5 g2 = -0.2 objs = [galsim.Exponential(half_light_radius=5.5)] def _psf_function(*, x, y): return galsim.Gaussian(fwhm=0.9) uv_offsets = [galsim.PositionD(x=-1.3, y=0.578)] uv_cen = galsim.PositionD(x=img_cen * scale, y=img_cen * scale) wcs = galsim.PixelScale(scale) se_img = render_objs_with_psf_shear( objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets, uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method, g1=g1, g2=g2, shear_scene=not shear_scene) se_img_shear_scene = render_objs_with_psf_shear( objs=objs, psf_function=_psf_function, uv_offsets=uv_offsets, uv_cen=uv_cen, wcs=wcs, img_dim=img_dim, method=method, g1=g1, g2=g2, shear_scene=shear_scene) if shear_scene: expected_img = galsim.Convolve( objs[0].shift(uv_offsets[0]).shear(g1=g1, g2=g2), galsim.Gaussian(fwhm=0.9) ).drawImage( nx=img_dim, ny=img_dim, wcs=wcs.local(world_pos=uv_cen)) else: expected_img = galsim.Convolve( objs[0].shear(g1=g1, g2=g2).shift(uv_offsets[0]), galsim.Gaussian(fwhm=0.9) ).drawImage( nx=img_dim, ny=img_dim, wcs=wcs.local(world_pos=uv_cen)) assert not np.allclose(expected_img.array, se_img.array, rtol=0, atol=1e-9) assert np.allclose( expected_img.array, se_img_shear_scene.array, rtol=0, atol=1e-9)
def test_shear_position_image_integration_pixelwcs(): wcs = galsim.PixelScale(0.3) obj1 = galsim.Gaussian(sigma=3) obj2 = galsim.Gaussian(sigma=2) pos2 = galsim.PositionD(3, 5) sum = obj1 + obj2.shift(pos2) shear = galsim.Shear(g1=0.1, g2=0.18) im1 = galsim.Image(50, 50, wcs=wcs) sum.shear(shear).drawImage(im1, center=im1.center) # Equivalent to shear each object separately and drawing at the sheared position. im2 = galsim.Image(50, 50, wcs=wcs) obj1.shear(shear).drawImage(im2, center=im2.center) obj2.shear(shear).drawImage( im2, add_to_image=True, center=im2.center + wcs.toImage(pos2.shear(shear)), ) print("err:", np.max(np.abs(im1.array - im2.array))) assert np.allclose(im1.array, im2.array, rtol=0, atol=5e-8)
def view(self, scale=None, wcs=None, origin=None, center=None, make_const=False): """Make a view of this image, which lets you change the scale, wcs, origin, etc. but view the same underlying data as the original image. If you do not provide either `scale` or `wcs`, the view will keep the same wcs as the current Image object. @param scale If provided, use this as the pixel scale for the image. [default: None] @param wcs If provided, use this as the wcs for the image. [default: None] @param origin If profided, use this as the origin position of the view. [default: None] @param center If profided, use this as the center position of the view. [default: None] @param make_const Make the view's data array immutable. [default: False] """ if origin is not None and center is not None: raise TypeError("Cannot provide both center and origin") if scale is not None: if wcs is not None: raise TypeError("Cannot provide both scale and wcs") wcs = galsim.PixelScale(scale) elif wcs is not None: if not isinstance(wcs,galsim.BaseWCS): raise TypeError("wcs parameters must be a galsim.BaseWCS instance") else: wcs = self.wcs if make_const: ret = Image(image=_galsim.ConstImageView[self.dtype](self.image.view()), wcs=wcs) else: ret = Image(image=self.image.view(), wcs=wcs) if origin is not None: ret.setOrigin(origin) elif center is not None: ret.setCenter(center) return ret
def test_mask_stars_raises(): rng = np.random.RandomState(seed=11) stars = np.array([(75, 85, 20)], dtype=[("x", "f8"), ("y", "f8"), ("radius_pixels", "f8")]) dim = 215 cdata = { "image": rng.normal(size=(dim, dim)), "weight": np.ones((dim, dim)), "mfrac": np.zeros((dim, dim)), "ormask": np.zeros((dim, dim), dtype=np.int32), "bmask": np.zeros((dim, dim), dtype=np.int32), "noise": rng.normal(size=(dim, dim)), "psf": rng.normal(size=(dim, dim)), } mbobs = make_mbobs_from_coadd_data( wcs=galsim.PixelScale(0.263).jacobian(), cdata=cdata, ) rng = np.random.RandomState(seed=10) with pytest.raises(RuntimeError): mask_stars( rng=rng, mbobs=mbobs, stars=stars, interp_cfg={ "skip": False, "iso_buff": 1, "fill_isolated_with_noise": False }, apodize_cfg={ "skip": False, "ap_rad": 1 }, mask_expand_rad=0, )
def test_exceptions(): """Test failure modes for InterpolatedImage class. """ import time t1 = time.time() try: # What if it receives as input something that is not an Image? Give it a GSObject to check. g = galsim.Gaussian(sigma=1.) np.testing.assert_raises(ValueError, galsim.InterpolatedImage, g) # What if Image does not have a scale set, but scale keyword is not specified? im = galsim.ImageF(5, 5) np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im) # Image must have bounds defined im = galsim.ImageF() im.scale = 1. np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im) # Weird flux normalization im = galsim.ImageF(5, 5, scale=1.) np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im, normalization = 'foo') # scale and WCS np.testing.assert_raises(TypeError, galsim.InterpolatedImage, im, wcs = galsim.PixelScale(1.), scale=1.) # weird WCS np.testing.assert_raises(TypeError, galsim.InterpolatedImage, im, wcs = 1.) # Weird interpolant - give it something random like a GSObject np.testing.assert_raises(Exception, galsim.InterpolatedImage, im, x_interpolant = g) # Image has wrong type im = galsim.ImageI(5, 5) np.testing.assert_raises(ValueError, galsim.InterpolatedImage, im) except ImportError: print 'The assert_raises tests require nose' t2 = time.time() print 'time for %s = %.2f'%(funcname(),t2-t1)
def test_coadd_image_correct(crazy_wcs, crazy_obj): rng = np.random.RandomState(seed=42) n_coadd = 10 psf_dim = 51 coadd_dim = 53 coadd_cen = (coadd_dim + 1) / 2 se_dim = int(np.ceil(coadd_dim * np.sqrt(2))) if se_dim % 2 == 0: se_dim += 1 se_cen = (se_dim + 1) / 2 scale = 0.2 noise_std = 0.1 world_origin = galsim.CelestialCoord(0 * galsim.degrees, 0 * galsim.degrees) aff = galsim.PixelScale(scale).affine() aff = aff.withOrigin(galsim.PositionD(coadd_cen, coadd_cen), galsim.PositionD(0, 0)) coadd_wcs = galsim.TanWCS( aff, world_origin, ) def _gen_psf_func(wcs, fwhm): def _psf_func(*args, **kargs): return galsim.Gaussian(fwhm=fwhm).drawImage( nx=101, ny=101, wcs=wcs.local(world_pos=world_origin)), galsim.PositionD(0, 0) return _psf_func wgts = [] objs = [] psf_objs = [] exps = [] for _ in range(n_coadd): if crazy_obj: _fwhm = 2.9 * (1.0 + rng.normal() * 0.1) _g1 = rng.normal() * 0.3 _g2 = rng.normal() * 0.3 obj = galsim.Gaussian(fwhm=_fwhm).shear(g1=_g1, g2=_g2) else: obj = galsim.Gaussian(fwhm=2.9).shear(g1=-0.1, g2=0.3) objs.append(obj) if crazy_wcs: shear = galsim.Shear(g1=rng.normal() * 0.01, g2=rng.normal() * 0.01) aff = galsim.ShearWCS(scale, shear).affine() aff = aff.withOrigin(galsim.PositionD(se_cen, se_cen), galsim.PositionD(0, 0)) wcs = galsim.TanWCS( aff, world_origin, ) else: aff = galsim.PixelScale(scale).affine() aff = aff.withOrigin(galsim.PositionD(se_cen, se_cen), galsim.PositionD(0, 0)) wcs = galsim.TanWCS( aff, world_origin, ) _noise = noise_std * (1 + (rng.uniform() - 0.5) * 2 * 0.05) wgts.append(1.0 / _noise**2) bmsk = galsim.ImageI(np.zeros((se_dim, se_dim))) img = obj.drawImage( nx=se_dim, ny=se_dim, wcs=wcs.local(world_pos=world_origin), ) if crazy_obj: _psf_fwhm = 1.0 * (1.0 + rng.normal() * 0.1) else: _psf_fwhm = 1.0 psf = galsim.Gaussian(fwhm=_psf_fwhm) psf_objs.append(psf) exp = make_exp( gsimage=img, bmask=bmsk, noise=_noise, galsim_wcs=wcs, galsim_psf=psf, psf_dim=psf_dim, ) exps.append(exp) coadd_bbox = geom.Box2I( geom.IntervalI(min=0, max=coadd_dim - 1), geom.IntervalI(min=0, max=coadd_dim - 1), ) coadd, exp_info = make_coadd_obs( exps=exps, coadd_wcs=make_dm_wcs(coadd_wcs), coadd_bbox=coadd_bbox, psf_dims=(psf_dim, ) * 2, rng=rng, remove_poisson=False, ) coadd_img = coadd.image coadd_psf = coadd.psf.image wgts = np.array(wgts) / np.sum(wgts) true_coadd_img = galsim.Sum([ obj.withFlux(wgt) for obj, wgt in zip(objs, wgts) ]).drawImage(nx=coadd_dim, ny=coadd_dim, wcs=coadd_wcs.local(world_pos=world_origin)).array true_coadd_psf = galsim.Sum([ obj.withFlux(wgt) for obj, wgt in zip(psf_objs, wgts) ]).drawImage(nx=psf_dim, ny=psf_dim, wcs=coadd_wcs.local(world_pos=world_origin)).array if not crazy_wcs: rtol = 0 atol = 5e-7 else: rtol = 0 atol = 5e-5 coadd_img_err = np.max(np.abs(coadd_img - true_coadd_img)) coadd_psf_err = np.max(np.abs(coadd_psf - true_coadd_psf)) print("image max abs error:", coadd_img_err) print("psf max abs error:", coadd_psf_err) if not np.allclose(coadd_img, true_coadd_img, rtol=rtol, atol=atol): _plot_cmp(coadd_img, true_coadd_img, rtol, atol, crazy_obj, crazy_wcs, "img") if not np.allclose(coadd_psf, true_coadd_psf, rtol=rtol, atol=atol): _plot_cmp(coadd_psf, true_coadd_psf, rtol, atol, crazy_obj, crazy_wcs, "psf") assert np.allclose(coadd_img, true_coadd_img, rtol=rtol, atol=atol) assert np.allclose(coadd_psf, true_coadd_psf, rtol=rtol, atol=atol) assert np.all(np.isfinite(coadd.noise))
def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None): """Determine a Piff PSF model for an exposure given a list of PSF candidates. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure containing the PSF candidates. psfCandidateList : `list` of `lsst.meas.algorithms.PsfCandidate` A sequence of PSF candidates typically obtained by detecting sources and then running them through a star selector. metadata : `lsst.daf.base import PropertyList` or `None`, optional A home for interesting tidbits of information. flagKey : `str` or `None`, optional Schema key used to mark sources actually used in PSF determination. Returns ------- psf : `lsst.meas.extensions.piff.PiffPsf` The measured PSF model. psfCellSet : `None` Unused by this PsfDeterminer. """ stars = [] for candidate in psfCandidateList: cmi = candidate.getMaskedImage() weight = computeWeight(cmi, self.config.maxSNR) bbox = cmi.getBBox() bds = galsim.BoundsI(galsim.PositionI(*bbox.getMin()), galsim.PositionI(*bbox.getMax())) gsImage = galsim.Image(bds, scale=1.0, dtype=float) gsImage.array[:] = cmi.image.array gsWeight = galsim.Image(bds, scale=1.0, dtype=float) gsWeight.array[:] = weight source = candidate.getSource() image_pos = galsim.PositionD(source.getX(), source.getY()) data = piff.StarData(gsImage, image_pos, weight=gsWeight) stars.append(piff.Star(data, None)) kernelSize = int( np.clip(self.config.kernelSize, self.config.kernelSizeMin, self.config.kernelSizeMax)) piffConfig = { 'type': "Simple", 'model': { 'type': 'PixelGrid', 'scale': self.config.samplingSize, 'size': kernelSize }, 'interp': { 'type': 'BasisPolynomial', 'order': self.config.spatialOrder }, 'outliers': { 'type': 'Chisq', 'nsigma': self.config.outlierNSigma, 'max_remove': self.config.outlierMaxRemove } } piffResult = piff.PSF.process(piffConfig) # Run on a single CCD, and in image coords rather than sky coords. wcs = {0: galsim.PixelScale(1.0)} pointing = None logger = logging.getLogger(self.log.getName() + ".Piff") logger.addHandler(lsst.log.LogHandler()) piffResult.fit(stars, wcs, pointing, logger=logger) psf = PiffPsf(kernelSize, kernelSize, piffResult) used_image_pos = [s.image_pos for s in piffResult.stars] if flagKey: for candidate in psfCandidateList: source = candidate.getSource() posd = galsim.PositionD(source.getX(), source.getY()) if posd in used_image_pos: source.set(flagKey, True) if metadata is not None: metadata.set("spatialFitChi2", piffResult.chisq) metadata.set("numAvailStars", len(stars)) metadata.set("numGoodStars", len(piffResult.stars)) metadata.set("avgX", np.mean([p.x for p in piffResult.stars])) metadata.set("avgY", np.mean([p.y for p in piffResult.stars])) return psf, None
def get_blend_shape(mu, c, e1, e2, hlr, flux, hsm=HLRShearModel(), wcs=None, pixel_scale=None, return_hlr=False, return_moments=False, out_unit='pixel'): """ Returns the combined shear of the blended system Not quite ready for multiple blended systems! TODO! [assuming N galaxies in the blended system] #A : the array of total NORMALIZED fluxes mu : the array (N vectors) of galaxy centers (i.e. the peaks of the Gaussians) c : the vector pointing to the luminosity center of the blended system e1 : the array of the first component of the shears for N galaxies e2 : the array of the second component of the shears for N galaxies flux : the flux of galaxies in the blend (in whatever unit or zeropoint but consistent) hlr in arcsec and mu,c in degrees. returns Q_blend in pixel^2, hlr_blend in arcsec """ if wcs is None: cen_ra = 0.5 * (mu[0].max() + mu[0].min()) * galsim.degrees cen_dec = 0.5 * (mu[1].max() + mu[1].min()) * galsim.degrees cen_coord = galsim.CelestialCoord(cen_ra, cen_dec) #, gsparams=gsp) affine_wcs = galsim.PixelScale(pixel_scale).affine().withOrigin( galsim.PositionI(0, 0)) wcs = galsim.TanWCS(affine_wcs, world_origin=cen_coord) #, gsparams=gsp) mu = galsim_world2pix(wcs, mu[0], mu[1]) c = galsim_world2pix(wcs, [c[0]], [c[1]]) # assumes scalar c[0], c[1] Sigma = get_shape_covmat_fast( hlr / pixel_scale, e1, e2, hsm=hsm ) # an array filled with second moments tensors of the blend members A = flux / (2 * np.pi * np.linalg.det(Sigma)**0.5) # compute the second moments of the blend "system" (collectively) Q_blend = get_blend_moments(A, mu, c, Sigma, unit='pixel') hlr_blend, e1_blend, e2_blend = hlr_from_moments_fast(Q_blend, hsm=hsm, return_shape=True) to_return = [e1_blend, e2_blend] if out_unit.startswith('deg'): convertor = pixel_scale * 3600 elif out_unit.startswith('arcmin'): convertor = pixel_scale * 60 elif out_unit.startswith('arcsec'): convertor = pixel_scale elif out_unit.startswith('pix'): convertor = 1.0 else: raise RuntimeError('Invalid `out_unit`') if return_hlr: to_return += [hlr_blend * convertor] if return_moments: to_return += [Q_blend * convertor**2] return to_return
def __init__(self, image, x_interpolant=None, k_interpolant=None, normalization='flux', scale=None, wcs=None, flux=None, pad_factor=4., noise_pad_size=0, noise_pad=0., rng=None, pad_image=None, calculate_stepk=True, calculate_maxk=True, use_cache=True, use_true_center=True, offset=None, gsparams=None, dx=None, _force_stepk=0., _force_maxk=0., _serialize_stepk=None, _serialize_maxk=None, hdu=None): # Check for obsolete dx parameter if dx is not None and scale is None: from galsim.deprecated import depr depr('dx', 1.1, 'scale') scale = dx # If the "image" is not actually an image, try to read the image as a file. if not isinstance(image, galsim.Image): image = galsim.fits.read(image, hdu=hdu) # make sure image is really an image and has a float type if image.dtype != np.float32 and image.dtype != np.float64: raise ValueError( "Supplied image does not have dtype of float32 or float64!") # it must have well-defined bounds, otherwise seg fault in SBInterpolatedImage constructor if not image.bounds.isDefined(): raise ValueError("Supplied image does not have bounds defined!") # check what normalization was specified for the image: is it an image of surface # brightness, or flux? if not normalization.lower() in ("flux", "f", "surface brightness", "sb"): raise ValueError(( "Invalid normalization requested: '%s'. Expecting one of 'flux', " + "'f', 'surface brightness', or 'sb'.") % normalization) # set up the interpolants if none was provided by user, or check that the user-provided ones # are of a valid type if x_interpolant is None: self.x_interpolant = galsim.Quintic(tol=1e-4) else: self.x_interpolant = galsim.utilities.convert_interpolant( x_interpolant) if k_interpolant is None: self.k_interpolant = galsim.Quintic(tol=1e-4) else: self.k_interpolant = galsim.utilities.convert_interpolant( k_interpolant) # Store the image as an attribute and make sure we don't change the original image # in anything we do here. (e.g. set scale, etc.) self.image = image.view() self.use_cache = use_cache # Set the wcs if necessary if scale is not None: if wcs is not None: raise TypeError( "Cannot provide both scale and wcs to InterpolatedImage") self.image.wcs = galsim.PixelScale(scale) elif wcs is not None: if not isinstance(wcs, galsim.BaseWCS): raise TypeError( "wcs parameter is not a galsim.BaseWCS instance") self.image.wcs = wcs elif self.image.wcs is None: raise ValueError( "No information given with Image or keywords about pixel scale!" ) # Set up the GaussianDeviate if not provided one, or check that the user-provided one is # of a valid type. if rng is None: if noise_pad: rng = galsim.BaseDeviate() elif not isinstance(rng, galsim.BaseDeviate): raise TypeError( "rng provided to InterpolatedImage constructor is not a BaseDeviate" ) # Check that given pad_image is valid: if pad_image: if isinstance(pad_image, str): pad_image = galsim.fits.read(pad_image) if not isinstance(pad_image, galsim.Image): raise ValueError("Supplied pad_image is not an Image!") if pad_image.dtype != np.float32 and pad_image.dtype != np.float64: raise ValueError( "Supplied pad_image is not one of the allowed types!") # Check that the given noise_pad is valid: try: noise_pad = float(noise_pad) except: pass if isinstance(noise_pad, float): if noise_pad < 0.: raise ValueError("Noise variance cannot be negative!") # There are other options for noise_pad, the validity of which will be checked in # the helper function self.buildNoisePadImage() # This will be passed to SBInterpolatedImage, so make sure it is the right type. pad_factor = float(pad_factor) if pad_factor <= 0.: raise ValueError("Invalid pad_factor <= 0 in InterpolatedImage") if use_true_center: im_cen = self.image.bounds.trueCenter() else: im_cen = self.image.bounds.center() local_wcs = self.image.wcs.local(image_pos=im_cen) self.min_scale = local_wcs.minLinearScale() self.max_scale = local_wcs.maxLinearScale() # Make sure the image fits in the noise pad image: if noise_pad_size: import math # Convert from arcsec to pixels according to the local wcs. # Use the minimum scale, since we want to make sure noise_pad_size is # as large as we need in any direction. noise_pad_size = int(math.ceil(noise_pad_size / self.min_scale)) # Round up to a good size for doing FFTs noise_pad_size = galsim._galsim.goodFFTSize(noise_pad_size) if noise_pad_size <= min(self.image.array.shape): # Don't need any noise padding in this case. noise_pad_size = 0 elif noise_pad_size < max(self.image.array.shape): noise_pad_size = max(self.image.array.shape) # See if we need to pad out the image with either a pad_image or noise_pad if noise_pad_size: new_pad_image = self.buildNoisePadImage(noise_pad_size, noise_pad, rng) if pad_image: # if both noise_pad and pad_image are set, then we need to build up a larger # pad_image and place the given pad_image in the center. # We will change the bounds here, so make a new view to avoid modifying the # input pad_image. pad_image = pad_image.view() pad_image.setCenter(0, 0) new_pad_image.setCenter(0, 0) if new_pad_image.bounds.includes(pad_image.bounds): new_pad_image[pad_image.bounds] = pad_image else: new_pad_image = pad_image pad_image = new_pad_image elif pad_image: # Just make sure pad_image is the right type pad_image = galsim.Image(pad_image, dtype=image.dtype) # Now place the given image in the center of the padding image: if pad_image: pad_image.setCenter(0, 0) self.image.setCenter(0, 0) if pad_image.bounds.includes(self.image.bounds): pad_image[self.image.bounds] = self.image pad_image.wcs = self.image.wcs else: # If padding was smaller than original image, just use the original image. pad_image = self.image else: pad_image = self.image # GalSim cannot automatically know what stepK and maxK are appropriate for the # input image. So it is usually worth it to do a manual calculation (below). # # However, there is also a hidden option to force it to use specific values of stepK and # maxK (caveat user!). The values of _force_stepk and _force_maxk should be provided in # terms of physical scale, e.g., for images that have a scale length of 0.1 arcsec, the # stepK and maxK should be provided in units of 1/arcsec. Then we convert to the 1/pixel # units required by the C++ layer below. Also note that profile recentering for even-sized # images (see the ._fix_center step below) leads to automatic reduction of stepK slightly # below what is provided here, while maxK is preserved. if _force_stepk > 0.: calculate_stepk = False _force_stepk *= self.min_scale if _force_maxk > 0.: calculate_maxk = False _force_maxk *= self.max_scale # Due to floating point rounding errors, for pickling it's necessary to store the exact # _force_maxk and _force_stepk used to create the SBInterpolatedImage, as opposed to the # values before being scaled by self.min_scale and self.max_scale. So we do that via the # _serialize_maxk and _serialize_stepk hidden kwargs, which should only get used during # pickling. if _serialize_stepk is not None: calculate_stepk = False _force_stepk = _serialize_stepk if _serialize_maxk is not None: calculate_maxk = False _force_maxk = _serialize_maxk # Save these values for pickling self._pad_image = pad_image self._pad_factor = pad_factor self._gsparams = gsparams # Make the SBInterpolatedImage out of the image. sbii = galsim._galsim.SBInterpolatedImage(pad_image.image, self.x_interpolant, self.k_interpolant, pad_factor, _force_stepk, _force_maxk, gsparams) # I think the only things that will mess up if getFlux() == 0 are the # calculateStepK and calculateMaxK functions, and rescaling the flux to some value. if (calculate_stepk or calculate_maxk or flux is not None) and sbii.getFlux() == 0.: raise RuntimeError( "This input image has zero total flux. " "It does not define a valid surface brightness profile.") if calculate_stepk: if calculate_stepk is True: sbii.calculateStepK() else: # If not a bool, then value is max_stepk sbii.calculateStepK(max_stepk=calculate_stepk) if calculate_maxk: if calculate_maxk is True: sbii.calculateMaxK() else: # If not a bool, then value is max_maxk sbii.calculateMaxK(max_maxk=calculate_maxk) # If the user specified a surface brightness normalization for the input Image, then # need to rescale flux by the pixel area to get proper normalization. if flux is None and normalization.lower() in [ 'surface brightness', 'sb' ]: flux = sbii.getFlux() * local_wcs.pixelArea() # Save this intermediate profile self._sbii = sbii self._stepk = sbii.stepK() / self.min_scale self._maxk = sbii.maxK() / self.max_scale self._flux = flux self._serialize_stepk = sbii.stepK() self._serialize_maxk = sbii.maxK() prof = GSObject(sbii) # Make sure offset is a PositionD offset = prof._parse_offset(offset) # Apply the offset, and possibly fix the centering for even-sized images # Note reverse=True, since we want to fix the center in the opposite sense of what the # draw function does. prof = prof._fix_center(self.image.array.shape, offset, use_true_center, reverse=True) # Save the offset we will need when pickling. if hasattr(prof, 'offset'): self._offset = -prof.offset else: self._offset = None # Bring the profile from image coordinates into world coordinates prof = local_wcs.toWorld(prof) # If the user specified a flux, then set to that flux value. if flux is not None: prof = prof.withFlux(float(flux)) # Now, in order for these to pickle correctly if they are the "original" object in a # Transform object, we need to hide the current transformation. An easy way to do that # is to hide the SBProfile in an SBAdd object. sbp = galsim._galsim.SBAdd([prof.SBProfile]) GSObject.__init__(self, sbp)
def __init__( self, *, frac_ex=0, rng, sel_rng, gal_type, psf_type, scale, shear_scene=True, n_coadd=1, n_coadd_psf=None, n_coadd_msk=1, g1=0.02, g2=0.0, g1ex=0.02, g2ex=0.0, dim=225, buff=25, noise=180, ngal=45.0, n_bands=1, gal_grid=None, psf_kws=None, gal_kws=None, homogenize_psf=False, mask_and_interp=False, add_bad_columns=True, add_cosmic_rays=True, bad_columns_kws=None, interpolation_type='cubic', ngal_factor=None): self.rng = rng self.sel_rng = sel_rng self.frac_ex = frac_ex if self.frac_ex == 0: self.flag_ex = False else: self.flag_ex = True self.noise_rng = np.random.RandomState(seed=rng.randint(1, 2**32-1)) self.gal_type = gal_type self.psf_type = psf_type self.n_coadd = n_coadd self.n_coadd_msk = n_coadd_msk self.g1 = g1 self.g2 = g2 self.g1ex = g1ex self.g2ex = g2ex self.shear_scene = shear_scene self.dim = dim self.buff = buff self.ngal_factor = ngal_factor # assumed to be one band unless otherwise specified via the # galaxy type self.noise = [noise / np.sqrt(self.n_coadd)] * n_bands self.ngal = ngal self.gal_grid = gal_grid self.im_cen = (dim - 1) / 2 self.psf_kws = psf_kws self.gal_kws = gal_kws self.n_coadd_psf = n_coadd_psf or self.n_coadd self.homogenize_psf = homogenize_psf self.mask_and_interp = mask_and_interp self.add_bad_columns = add_bad_columns self.add_cosmic_rays = add_cosmic_rays self.bad_columns_kws = bad_columns_kws or {} self.interpolation_type = interpolation_type self.area_sqr_arcmin = ((self.dim - 2*self.buff) * scale / 60)**2 self._galsim_rng = galsim.BaseDeviate( seed=self.rng.randint(low=1, high=2**32-1)) # typical pixel scale self.scale = scale self.wcs = galsim.PixelScale(self.scale) # frac of a single dimension that is used for drawing objects frac = 1.0 - self.buff * 2 / self.dim # half of the width of center of the patch that has objects self.pos_width = self.dim * frac * 0.5 * self.scale # for wldeblend galaxies, we have to adjust some of the input # parameters since they are computed self consisently from the # input catalog and/or package defaults if self.gal_type == 'wldeblend': self._extra_init_for_wldeblend() # given the input number of objects to simulate per square arcminute, # compute the number we actually need if self.ngal_factor is None: self.ngal_factor = 1 LOGGER.info('ngal adjustment factor: %f', self.ngal_factor) self.nobj = int( self.ngal * self.ngal_factor * (self.dim * self.scale / 60 * frac)**2) self.shear_mat = galsim.Shear(g1=self.g1, g2=self.g2).getMatrix() self.shear_matex = galsim.Shear(g1=self.g1ex, g2=self.g2ex).getMatrix() if self.gal_grid is not None: self.nobj = self.gal_grid * self.gal_grid self.n_bands = len(self.noise) LOGGER.info('simulating %d bands', self.n_bands)
def distributed_strategy(args): print( f"Started worker {THIS_WORKER} at {datetime.now().strftime('%y-%m-%d_%H-%M-%S')}" ) options = tf.io.TFRecordOptions(compression_type=args.compression_type) catalog = galsim.COSMOSCatalog(sample=args.sample, dir=args.cosmos_dir, exclusion_level=args.exclusion_level, min_flux=args.min_flux) n_galaxies = catalog.getNObjects() cat_param = catalog.param_cat[catalog.orig_index] sparams = cat_param['sersicfit'] cat_param = append_fields(cat_param, 'sersic_q', sparams[:, 3]) cat_param = append_fields(cat_param, 'sersic_n', sparams[:, 2]) with tf.io.TFRecordWriter( os.path.join(args.output_dir, f"data_{THIS_WORKER}.tfrecords"), options) as writer: for index in range((THIS_WORKER - 1), n_galaxies, N_WORKERS): gal = catalog.makeGalaxy(index, noise_pad_size=args.pixels * args.pixel_scale) psf = gal.original_psf # We save the corresponding attributes for this galaxy if hasattr(args, 'attributes'): params = cat_param[index] attributes = {k: params[k] for k in args.attributes} else: attributes = None # Apply the PSF gal = galsim.Convolve(gal, psf) # Compute sqrt of absolute noise power spectrum, at the resolution and stamp size of target image ps = gal.noise._get_update_rootps( (args.pixels, args.pixels), wcs=galsim.PixelScale(args.pixel_scale)) # We draw the pixel image of the convolved image im = gal.drawImage(nx=args.pixels, ny=args.pixels, scale=args.pixel_scale, method='no_pixel', use_true_center=False).array.astype('float32') # preprocess image # For correlated noise, we estimate that the sqrt of the Energy Spectral Density of the noise at (f_x=f_y=0) # is a good estimate of the STD if args.preprocess: im = tf.nn.relu(im - ps[0, 0]).numpy( ) # subtract background, fold negative pixels to 0 im /= im.max() # normalize peak to 1 signal_pixels = np.sum( im > args.signal_threshold ) # how many pixels have a value above a certain threshold if signal_pixels < args.signal_pixels: # argument used to select only examples that are more distinct galaxy features (it does however bias the dataset in redshift space) continue # Draw a kimage of the galaxy, just to figure out what maxk is, there might # be more efficient ways to do this though... bounds = galsim.BoundsI(0, args.pixels // 2, -args.pixels // 2, args.pixels // 2 - 1) imG = gal.drawKImage(bounds=bounds, scale=2. * np.pi / (args.pixels * args.pixels), recenter=False) mask = ~(np.fft.fftshift(imG.array, axes=0) == 0) # Draw the Fourier domain image of the galaxy, using x1 zero padding, # and x2 subsampling interp_factor = 2 padding_factor = 1 Nk = args.pixels * interp_factor * padding_factor bounds = galsim.BoundsI(0, Nk // 2, -Nk // 2, Nk // 2 - 1) imCp = psf.drawKImage(bounds=bounds, scale=2. * np.pi / (Nk * args.pixel_scale / interp_factor), recenter=False) # Transform the psf array into proper format, remove the phase im_psf = np.abs(np.fft.fftshift(imCp.array, axes=0)).astype('float32') # The following comes from correlatednoise.py rt2 = np.sqrt(2.) shape = (args.pixels, args.pixels) ps[0, 0] = rt2 * ps[0, 0] # Then make the changes necessary for even sized arrays if shape[1] % 2 == 0: # x dimension even ps[0, shape[1] // 2] = rt2 * ps[0, shape[1] // 2] if shape[0] % 2 == 0: # y dimension even ps[shape[0] // 2, 0] = rt2 * ps[shape[0] // 2, 0] # Both dimensions even if shape[1] % 2 == 0: ps[shape[0] // 2, shape[1] // 2] = rt2 * ps[shape[0] // 2, shape[1] // 2] # Apply mask to power spectrum so that it is very large outside maxk ps = np.where(mask, np.log(ps**2), 10).astype('float32') features = { "image": _bytes_feature(im.tobytes()), "height": _int64_feature(im.shape[0]), "width": _int64_feature(im.shape[1]), "psf": _bytes_feature(im_psf.tobytes()), "ps": _bytes_feature(ps.tobytes()), # power spectrum of the noise } # Adding the parameters provided if attributes is not None: for k in attributes: features['attrs_' + k] = _float_feature(attributes[k]) record = tf.train.Example(features=tf.train.Features( feature=features)).SerializeToString() writer.write(record) print( f"Finished worker {THIS_WORKER} at {datetime.now().strftime('%y-%m-%d_%H-%M-%S')}" )
def get_exp_list_coadd(m, i, m2=None): def make_jacobian(dudx, dudy, dvdx, dvdy, x, y): j = galsim.JacobianWCS(dudx, dudy, dvdx, dvdy) return j.withOrigin(galsim.PositionD(x, y)) oversample = 1 #def psf_offset(i,j,star_): m3 = [0] #relative_offset=[0] for jj, psf_ in enumerate(m2): # m2 has psfs for each observation. if jj == 0: continue gal_stamp_center_row = m['orig_start_row'][i][jj] + m['box_size'][ i] / 2 - 0.5 # m['box_size'] is the galaxy stamp size. gal_stamp_center_col = m['orig_start_col'][i][jj] + m['box_size'][ i] / 2 - 0.5 # m['orig_start_row/col'] is in SCA coordinates. psf_stamp_size = 32 # Make the bounds for the psf stamp. b = galsim.BoundsI( xmin=(m['orig_start_col'][i][jj] + (m['box_size'][i] - 32) / 2. - 1) * oversample + 1, xmax=(m['orig_start_col'][i][jj] + (m['box_size'][i] - 32) / 2. + psf_stamp_size - 1) * oversample, ymin=(m['orig_start_row'][i][jj] + (m['box_size'][i] - 32) / 2. - 1) * oversample + 1, ymax=(m['orig_start_row'][i][jj] + (m['box_size'][i] - 32) / 2. + psf_stamp_size - 1) * oversample) # Make wcs for oversampled psf. wcs_ = make_jacobian( m.get_jacobian(i, jj)['dudcol'] / oversample, m.get_jacobian(i, jj)['dudrow'] / oversample, m.get_jacobian(i, jj)['dvdcol'] / oversample, m.get_jacobian(i, jj)['dvdrow'] / oversample, m['orig_col'][i][jj] * oversample, m['orig_row'][i][jj] * oversample) # Taken from galsim/roman_psfs.py line 266. Update each psf to an object-specific psf using the wcs. scale = galsim.PixelScale(wfirst.pixel_scale / oversample) psf_ = wcs_.toWorld(scale.toImage(psf_), image_pos=galsim.PositionD(wfirst.n_pix / 2, wfirst.n_pix / 2)) # Convolve with the star model and get the psf stamp. #st_model = galsim.DeltaFunction(flux=1.) #st_model = st_model.evaluateAtWavelength(wfirst.getBandpasses(AB_zeropoint=True)['H158'].effective_wavelength) #st_model = st_model.withFlux(1.) #st_model = galsim.Convolve(st_model, psf_) psf_ = galsim.Convolve(psf_, galsim.Pixel(wfirst.pixel_scale)) psf_stamp = galsim.Image(b, wcs=wcs_) # Galaxy is being drawn with some subpixel offsets, so we apply the offsets when drawing the psf too. offset_x = m['orig_col'][i][jj] - gal_stamp_center_col offset_y = m['orig_row'][i][jj] - gal_stamp_center_row offset = galsim.PositionD(offset_x, offset_y) if (offset_x <= -1.0 or offset_y <= -1.0): print(offset) elif (offset_x >= 1.0 or offset_y >= 1.0): print(offset) psf_.drawImage(image=psf_stamp, offset=offset, method='no_pixel') m3.append(psf_stamp.array) obs_list = ObsList() psf_list = ObsList() included = [] w = [] # For each of these objects create an observation for j in range(m['ncutout'][i]): if j == 0: continue # if j>1: # continue im = m.get_cutout(i, j, type='image') weight = m.get_cutout(i, j, type='weight') im_psf = m3[j] im_psf2 = im_psf if np.sum(im) == 0.: #print(local_meds, i, j, np.sum(im)) print('no flux in image ', i, j) continue jacob = m.get_jacobian(i, j) # Get a galaxy jacobian. gal_jacob = Jacobian( row=(m['orig_row'][i][j] - m['orig_start_row'][i][j]), col=(m['orig_col'][i][j] - m['orig_start_col'][i][j]), dvdrow=jacob['dvdrow'], dvdcol=jacob['dvdcol'], dudrow=jacob['dudrow'], dudcol=jacob['dudcol']) psf_center = (32 / 2.) + 0.5 # Get a oversampled psf jacobian. if oversample == 1: psf_jacob2 = Jacobian( row=15.5 + (m['orig_row'][i][j] - m['orig_start_row'][i][j] + 1 - (m['box_size'][i] / 2. + 0.5)) * oversample, col=15.5 + (m['orig_col'][i][j] - m['orig_start_col'][i][j] + 1 - (m['box_size'][i] / 2. + 0.5)) * oversample, dvdrow=jacob['dvdrow'] / oversample, dvdcol=jacob['dvdcol'] / oversample, dudrow=jacob['dudrow'] / oversample, dudcol=jacob['dudcol'] / oversample) elif oversample == 4: psf_jacob2 = Jacobian( row=63.5 + (m['orig_row'][i][j] - m['orig_start_row'][i][j] + 1 - (m['box_size'][i] / 2. + 0.5)) * oversample, col=63.5 + (m['orig_col'][i][j] - m['orig_start_col'][i][j] + 1 - (m['box_size'][i] / 2. + 0.5)) * oversample, dvdrow=jacob['dvdrow'] / oversample, dvdcol=jacob['dvdcol'] / oversample, dudrow=jacob['dudrow'] / oversample, dudcol=jacob['dudcol'] / oversample) # Create an obs for each cutout mask = np.where(weight != 0) if 1. * len(weight[mask]) / np.product(np.shape(weight)) < 0.8: continue w.append(np.mean(weight[mask])) noise = np.ones_like(weight) / w[-1] psf_obs = Observation(im_psf, jacobian=gal_jacob, meta={ 'offset_pixels': None, 'file_id': None }) psf_obs2 = Observation(im_psf2, jacobian=psf_jacob2, meta={ 'offset_pixels': None, 'file_id': None }) #obs = Observation(im, weight=weight, jacobian=gal_jacob, psf=psf_obs, meta={'offset_pixels':None,'file_id':None}) # oversampled PSF obs = Observation(im, weight=weight, jacobian=gal_jacob, psf=psf_obs2, meta={ 'offset_pixels': None, 'file_id': None }) obs.set_noise(noise) obs_list.append(obs) psf_list.append(psf_obs2) included.append(j) return obs_list, psf_list, np.array(included) - 1, np.array(w)
def BuildWCS(config, logger=None): """Read the wcs from the config dict, writing both it and, if it is well-defined, the pixel_scale to the config. If the wcs does not have a well-defined pixel_scale, it will be stored as None. """ image = config['image'] # If there is a wcs field, read it and update the wcs variable. if 'wcs' in image: image_wcs = image['wcs'] if 'type' in image_wcs: type = image_wcs['type'] else: type = 'PixelScale' # Special case: origin == center means to use image_center for the wcs origin if 'origin' in image_wcs and image_wcs['origin'] == 'center': origin = config['image_center'] if logger: logger.debug('image %d: Using origin = %s', config['image_num'], str(origin)) image_wcs['origin'] = origin if type not in valid_wcs_types: raise AttributeError("Invalid image.wcs.type=%s." % type) if 'origin' in image_wcs or 'world_origin' in image_wcs: build_func = eval(valid_wcs_types[type][1]) else: build_func = eval(valid_wcs_types[type][0]) if logger: logger.debug('image %d: Build WCS for type = %s using %s', config['image_num'], type, str(build_func)) req = build_func._req_params opt = build_func._opt_params single = build_func._single_params # Pull in the image layer pixel_scale as a scale item if necessary. if (('scale' in req or 'scale' in opt) and 'scale' not in image_wcs and 'pixel_scale' in image): image_wcs['scale'] = image['pixel_scale'] kwargs, safe = galsim.config.GetAllParams(image_wcs, type, config, req, opt, single) if logger and build_func._takes_logger: kwargs['logger'] = logger # This would be weird, but might as well check... if build_func._takes_rng: if 'rng' not in config: raise ValueError( "No config['rng'] available for %s.type = %s" % (key, type)) kwargs['rng'] = config['rng'] if logger: logger.debug('image %d: kwargs = %s', config['image_num'], str(kwargs)) wcs = build_func(**kwargs) else: # Default if no wcs is to use PixelScale if 'pixel_scale' in image: scale = galsim.config.ParseValue(image, 'pixel_scale', config, float)[0] else: scale = 1.0 wcs = galsim.PixelScale(scale) # Write it to the config dict and also return it. config['wcs'] = wcs # If the WCS is a PixelScale or OffsetWCS, then store the pixel_scale in base. The # config apparatus does not use it -- we always use the wcs -- but we keep it in case # the user wants to use it for an Eval item. It's one of the variables they are allowed # to assume will be present for them. if wcs.isPixelScale(): config['pixel_scale'] = wcs.scale return wcs