def _convertMask(image, weight=None, badpix=None): """Convert from input weight and badpix images to a single mask image needed by C++ functions. This is used by EstimateShear() and FindAdaptiveMom(). """ # if no weight image was supplied, make an int array (same size as gal image) filled with 1's if weight is None: mask = galsim.ImageI(bounds=image.bounds, init_value=1) else: # if weight image was supplied, check if it has the right bounds and is non-negative if weight.bounds != image.bounds: raise ValueError( "Weight image does not have same bounds as the input Image!") # also make sure there are no negative values import numpy as np if np.any(weight.array < 0) == True: raise ValueError("Weight image cannot contain negative values!") # if weight is an ImageI, then we can use it as the mask image: import numpy if weight.dtype == numpy.int32: if not badpix: mask = weight else: # If we need to mask bad pixels, we'll need a copy anyway. mask = galsim.ImageI(weight) # otherwise, we need to convert it to the right type else: mask = galsim.ImageI(bounds=image.bounds, init_value=0) mask.array[weight.array > 0.] = 1 # if badpix image was supplied, identify the nonzero (bad) pixels and set them to zero in weight # image; also check bounds if badpix is not None: if badpix.bounds != image.bounds: raise ValueError( "Badpix image does not have the same bounds as the input Image!" ) import numpy as np mask.array[badpix.array != 0] = 0 # if no pixels are used, raise an exception if mask.array.sum() == 0: raise RuntimeError("No pixels are being used!") # finally, return the Image for the weight map return mask.image.view()
def _convertImage(image): """Convert the given image to the correct format needed to pass to the C++ layer. This is used by EstimateShear() and FindAdaptiveMom(). """ # if weight is an ImageS, then convert to ImageI. if image.dtype == np.int16: image = galsim.ImageI(image) # Return this as an ImageView return image.image.view()
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 __init__(self, images, weight=None, badpix=None, seg=None, psf=None, wcs=None, id=0): # Check that images is valid if not isinstance(images, list): raise TypeError('images should be a list') if len(images) == 0: raise ValueError('no cutouts in this object') # Check that the box sizes are valid for i in range(len(images)): s = images[i].array.shape if s[0] != s[1]: raise ValueError('Array shape %s is invalid. Must be square' % (str(s))) if s[0] not in BOX_SIZES: raise ValueError( 'Array shape %s is invalid. Size must be in %s' % (str(box_size), str(BOX_SIZES))) if i > 0 and s != images[0].array.shape: raise ValueError('Images must all be the same shape') # The others are optional, but if given, make sure they are ok. for lst, name, isim in [(weight, 'weight', True), (badpix, 'badpix', True), (seg, 'seg', True), (psf, 'psf', False), (wcs, 'wcs', False)]: if lst is not None: if not isinstance(lst, list): raise TypeError('%s should be a list' % name) if len(lst) != len(images): raise ValueError('%s is the wrong length' % name) if isim: for i in range(len(images)): im1 = lst[i] im2 = images[i] if (im1.array.shape != im2.array.shape): raise ValueError("%s[%d] has the wrong shape." % (name, i)) # The PSF images don't have to be the same shape as the main images. # But make sure all psf images are square and the same shape if psf is not None: s = psf[i].array.shape if s[0] != s[1]: raise ValueError( 'PSF array shape %s is invalid. Must be square' % (str(s))) if s[0] not in BOX_SIZES: raise ValueError( 'PSF array shape %s is invalid. Size must be in %s' % (str(box_size), str(BOX_SIZES))) if i > 0 and s != psf[0].array.shape: raise ValueError('PSF images must all be the same shape') # Check that wcs are Uniform and convert them to AffineTransforms in case they aren't. if wcs is not None: for i in range(len(wcs)): if not isinstance(wcs[i], galsim.wcs.UniformWCS): raise TypeError( 'wcs list should contain UniformWCS objects') elif not isinstance(wcs[i], galsim.AffineTransform): wcs[i] = wcs[i].affine() self.id = id self.images = [im.view() for im in images] # Convert to 0-based images as preferred by meds. for im in self.images: # Note: making the list of views above means this won't change the originals. im.setOrigin(0, 0) self.box_size = self.images[0].array.shape[0] self.n_cutouts = len(self.images) if psf is not None: self.psf_box_size = self.images[0].array.shape[0] else: self.psf_box_size = 0 # If weight is not provided, create something sensible. if weight is not None: self.weight = weight else: self.weight = [ galsim.Image(self.box_size, self.box_size, init_value=1) ] * self.n_cutouts # If badpix is provided, combine it into the weight image. if badpix is not None: for i in range(len(badpix)): mask = [badpix[i] != 0] self.weight[i][mask] = 0. # If seg is not provided, use all 1's. if seg is not None: self.seg = seg else: self.seg = [ galsim.ImageI(self.box_size, self.box_size, init_value=1) ] * self.n_cutouts # If wcs is not provided, get it from the images. if wcs is not None: self.wcs = wcs else: self.wcs = [ im.wcs.affine(image_pos=im.trueCenter()) for im in self.images ] # psf is not required, so leave it as None if not provided. self.psf = psf
def test_masks(): """Test that moments and shear estimation routines respond appropriately to masks.""" # set up some toy galaxy and PSF my_sigma = 1.0 my_pixscale = 0.1 my_g1 = 0.15 my_g2 = -0.4 imsize = 256 g = galsim.Gaussian(sigma=my_sigma) p = galsim.Gaussian( sigma=my_sigma ) # the ePSF is Gaussian (kind of silly but it means we can # predict results exactly) g = g.shear(g1=my_g1, g2=my_g2) obj = galsim.Convolve(g, p) im = galsim.ImageF(imsize, imsize) p_im = galsim.ImageF(imsize, imsize) obj.drawImage(image=im, scale=my_pixscale, method='no_pixel') p.drawImage(image=p_im, scale=my_pixscale, method='no_pixel') # make some screwy weight and badpix images that should cause issues, and check that the # exception is thrown good_weight_im = galsim.ImageI(imsize, imsize, init_value=1) try: ## different size from image weight_im = galsim.ImageI(imsize, 2 * imsize) np.testing.assert_raises(ValueError, galsim.hsm.FindAdaptiveMom, im, weight_im) np.testing.assert_raises(ValueError, galsim.hsm.EstimateShear, im, p_im, weight_im) badpix_im = galsim.ImageI(imsize, 2 * imsize) np.testing.assert_raises(ValueError, galsim.hsm.FindAdaptiveMom, im, badpix_im) np.testing.assert_raises(ValueError, galsim.hsm.EstimateShear, im, p_im, good_weight_im, badpix_im) ## weird values weight_im = galsim.ImageI(imsize, imsize, init_value=-3) np.testing.assert_raises(ValueError, galsim.hsm.FindAdaptiveMom, im, weight_im) np.testing.assert_raises(ValueError, galsim.hsm.EstimateShear, im, p_im, weight_im) ## excludes all pixels weight_im = galsim.ImageI(imsize, imsize) np.testing.assert_raises(RuntimeError, galsim.hsm.FindAdaptiveMom, im, weight_im) np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, im, p_im, weight_im) badpix_im = galsim.ImageI(imsize, imsize, init_value=-1) np.testing.assert_raises(RuntimeError, galsim.hsm.FindAdaptiveMom, im, good_weight_im, badpix_im) np.testing.assert_raises(RuntimeError, galsim.hsm.EstimateShear, im, p_im, good_weight_im, badpix_im) except ImportError: # assert_raises requires nose, which we don't want to force people to install. # So if they are running this without nose, we just skip these tests. pass # check moments, shear without mask resm = im.FindAdaptiveMom() ress = galsim.hsm.EstimateShear(im, p_im) # check moments, shear with weight image that includes all pixels weightall1 = galsim.ImageI(imsize, imsize, init_value=1) resm_weightall1 = im.FindAdaptiveMom(weightall1) ress_weightall1 = galsim.hsm.EstimateShear(im, p_im, weightall1) # We'll do this series of tests a few times, so encapsulate the code here. def check_equal(resm, ress, resm_test, ress_test, tag): np.testing.assert_equal(resm.observed_shape.e1, resm_test.observed_shape.e1, err_msg="e1 from FindAdaptiveMom changes " + tag) np.testing.assert_equal(resm.observed_shape.e2, resm_test.observed_shape.e2, err_msg="e2 from FindAdaptiveMom changes " + tag) np.testing.assert_equal(resm.moments_sigma, resm_test.moments_sigma, err_msg="sigma from FindAdaptiveMom changes " + tag) np.testing.assert_equal( ress.observed_shape.e1, ress_test.observed_shape.e1, err_msg="observed e1 from EstimateShear changes " + tag) np.testing.assert_equal( ress.observed_shape.e2, ress_test.observed_shape.e2, err_msg="observed e2 from EstimateShear changes " + tag) np.testing.assert_equal( ress.moments_sigma, ress_test.moments_sigma, err_msg="observed sigma from EstimateShear changes " + tag) np.testing.assert_equal( ress.corrected_e1, ress_test.corrected_e1, err_msg="corrected e1 from EstimateShear changes " + tag) np.testing.assert_equal( ress.corrected_e2, ress_test.corrected_e2, err_msg="corrected e2 from EstimateShear changes " + tag) np.testing.assert_equal( ress.resolution_factor, ress_test.resolution_factor, err_msg="resolution factor from EstimateShear changes " + tag) check_equal(resm, ress, resm_weightall1, ress_weightall1, "when using inclusive weight") # check moments and shears with mask of edges, should be nearly the same # (this seems dumb, but it's helpful for keeping track of whether the pointers in the C++ code # are being properly updated despite the masks. If we monkey in that code again, it will be a # useful check.) maskedge = galsim.ImageI(imsize, imsize, init_value=1) xmin = maskedge.xmin xmax = maskedge.xmax ymin = maskedge.ymin ymax = maskedge.ymax edgenum = 3 for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): maskedge.setValue(ind1, ind2, 0) resm_maskedge = im.FindAdaptiveMom(maskedge) ress_maskedge = galsim.hsm.EstimateShear(im, p_im, maskedge) test_decimal = 4 np.testing.assert_almost_equal( resm.observed_shape.e1, resm_maskedge.observed_shape.e1, decimal=test_decimal, err_msg="e1 from FindAdaptiveMom changes when masking edge") np.testing.assert_almost_equal( resm.observed_shape.e2, resm_maskedge.observed_shape.e2, decimal=test_decimal, err_msg="e2 from FindAdaptiveMom changes when masking edge") np.testing.assert_almost_equal( resm.moments_sigma, resm_maskedge.moments_sigma, decimal=test_decimal, err_msg="sigma from FindAdaptiveMom changes when masking edge") np.testing.assert_almost_equal( ress.observed_shape.e1, ress_maskedge.observed_shape.e1, decimal=test_decimal, err_msg="observed e1 from EstimateShear changes when masking edge") np.testing.assert_almost_equal( ress.observed_shape.e2, ress_maskedge.observed_shape.e2, decimal=test_decimal, err_msg="observed e2 from EstimateShear changes when masking edge") np.testing.assert_almost_equal( ress.moments_sigma, ress_maskedge.moments_sigma, decimal=test_decimal, err_msg="observed sigma from EstimateShear changes when masking edge") np.testing.assert_almost_equal( ress.corrected_e1, ress_maskedge.corrected_e1, decimal=test_decimal, err_msg="corrected e1 from EstimateShear changes when masking edge") np.testing.assert_almost_equal( ress.corrected_e2, ress_maskedge.corrected_e2, decimal=test_decimal, err_msg="corrected e2 from EstimateShear changes when masking edge") np.testing.assert_almost_equal( ress.resolution_factor, ress_maskedge.resolution_factor, decimal=test_decimal, err_msg="resolution factor from EstimateShear changes when masking edge" ) # check that results don't change *at all* i.e. using assert_equal when we do this edge masking # in different ways: ## do the same as the previous test, but with weight map that is floats (0.0 or 1.0) maskedge = galsim.ImageF(imsize, imsize, init_value=1.) for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): maskedge.setValue(ind1, ind2, 0.) resm_maskedge1 = im.FindAdaptiveMom(maskedge) ress_maskedge1 = galsim.hsm.EstimateShear(im, p_im, maskedge) check_equal(resm_maskedge, ress_maskedge, resm_maskedge1, ress_maskedge1, "when masking with floats") ## make the weight map for allowed pixels a nonzero value that also != 1 maskedge = galsim.ImageF(imsize, imsize, init_value=2.3) for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): maskedge.setValue(ind1, ind2, 0.) resm_maskedge1 = im.FindAdaptiveMom(maskedge) ress_maskedge1 = galsim.hsm.EstimateShear(im, p_im, maskedge) check_equal(resm_maskedge, ress_maskedge, resm_maskedge1, ress_maskedge1, "when masking with floats != 1") ## make the weight map all equal to 1, and use a badpix map with a range of nonzero values maskedge = galsim.ImageI(imsize, imsize, init_value=1) badpixedge = galsim.ImageI(imsize, imsize, init_value=0) for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): badpixedge.setValue(ind1, ind2, ind1 + 1) resm_maskedge1 = im.FindAdaptiveMom(maskedge, badpixedge) ress_maskedge1 = galsim.hsm.EstimateShear(im, p_im, maskedge, badpixedge) check_equal(resm_maskedge, ress_maskedge, resm_maskedge1, ress_maskedge1, "when masking with badpix") ## same as previous, but with badpix of floats maskedge = galsim.ImageI(imsize, imsize, init_value=1) badpixedge = galsim.ImageF(imsize, imsize, init_value=0.) for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): badpixedge.setValue(ind1, ind2, float(ind1 + 1)) resm_maskedge1 = im.FindAdaptiveMom(maskedge, badpixedge) ress_maskedge1 = galsim.hsm.EstimateShear(im, p_im, maskedge, badpixedge) check_equal(resm_maskedge, ress_maskedge, resm_maskedge1, ress_maskedge1, "when masking with badpix (floats)") ## do some of the masking using weight map, and the rest using badpix maskedge = galsim.ImageI(imsize, imsize, init_value=1) badpixedge = galsim.ImageI(imsize, imsize, init_value=0) meanval = int(0.5 * (xmin + xmax)) for ind1 in range(xmin, xmax + 1): for ind2 in range(ymin, ymax + 1): if (ind1 <= (xmin + edgenum)) or (ind1 >= (xmax - edgenum)) or ( ind2 <= (ymin + edgenum)) or (ind2 >= (ymax - edgenum)): if ind1 < meanval: badpixedge.setValue(ind1, ind2, 1) else: maskedge.setValue(ind1, ind2, 0) resm_maskedge1 = im.FindAdaptiveMom(maskedge, badpixedge) ress_maskedge1 = galsim.hsm.EstimateShear(im, p_im, maskedge, badpixedge) check_equal(resm_maskedge, ress_maskedge, resm_maskedge1, ress_maskedge1, "when masking with badpix and weight map")
def __init__(self, images, weights=None, badpix=None, segs=None, wcstrans=None, id=0): # assign the ID self.id = id # check if images is a list if not isinstance(images, list): raise TypeError('images should be a list') # get number of cutouts from image list self.images = images # get box size from the first image self.box_size = self.images[0].array.shape[0] self.n_cutouts = len(self.images) # see if there are cutouts if self.n_cutouts < 1: raise ValueError('no cutouts in this object') # check if the box size is correct if self.box_size not in BOX_SIZES: # raise ValueError('box size should be in [32,48,64,96,128,196,256], is %d' % box_size) raise ValueError('box size should be in ' + str(BOX_SIZES) + ', is ' + str(self.box_size)) # check if weights, segs and wcstrans were supplied. If not, create sensible values. if weights != None: self.weights = weights else: self.weights = [ galsim.ImageF(self.box_size, self.box_size, init_value=1) ] * self.n_cutouts # check segmaps if segs != None: self.segs = segs # I think eventually, the meds files will have some more sophisticated pixel map # where the segmentation info and bad pixel info are separately coded. # However, for now, we just set to 0 any bad pixels. # (Not that GalSim has any mechanism yet for generating bad pixels, so this is # usually a null op, but it's in place for when there is something to do.) if badpix != None: if len(self.segs) != len(badpix): raise ValueError("segs and badpix are different lengths") for i in range(len(self.segs)): if (self.segs[i].array.shape != badpix[i].array.shape): raise ValueError( "segs[%d] and badpix[%d] have different shapes." % (i, i)) self.segs[i].array[:, :] &= (badpix[i].array == 0) elif badpix != None: self.segs = badpix # Flip the sense of the bits 0 -> 1, other -> 0 # Again, this might need to become more sophisticated at some point... for i in range(len(self.segs)): self.segs[i].array[:, :] = (badpix[i].array == 0) else: self.segs = [ galsim.ImageI(self.box_size, self.box_size, init_value=1) ] * self.n_cutouts # check wcstrans if wcstrans != None: self.wcstrans = wcstrans else: # build jacobians that are just based on the pixel scale, set the centers dudrow = 1 dudcol = 0 dvdrow = 0 dvdcol = 1 # set to the center of the postage stamp row0 = float(self.box_size) / 2. col0 = float(self.box_size) / 2. self.wcstrans = [ WCSTransform(dudrow * im.scale, dudcol * im.scale, dvdrow * im.scale, dvdcol * im.scale, row0, col0) for im in self.images ] # check if weights,segs,jacks are lists if not isinstance(self.weights, list): raise TypeError('weights should be a list') if not isinstance(self.segs, list): raise TypeError('segs should be a list') if not isinstance(self.wcstrans, list): raise TypeError('wcstrans should be a list') # loop through the images and check if they are of the same size for extname in ('images', 'weights', 'segs'): # get the class field ext = eval('self.' + extname) # loop through exposures for icutout, cutout in enumerate(ext): # get the sizes of array nx = cutout.array.shape[0] ny = cutout.array.shape[1] # x and y size should be the same if nx != ny: raise ValueError('%s should be square and is %d x %d' % (extname, nx, ny)) # check if box size is correct if nx != self.box_size: raise ValueError( '%s object %d has size %d and should be %d' % (extname, icutout, nx, self.box_size)) # see if the number of Jacobians is right if len(self.wcstrans) != self.n_cutouts: raise ValueError( 'number of Jacobians is %d is not equal to number of cutouts %d' % (len(self.wcstrans), self.n_cutouts)) # check each Jacobian for jac in self.wcstrans: # should ba a WCSTransform instance if not isinstance(jac, WCSTransform): raise TypeError( 'wcstrans list should contain WCSTransform objects')
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 test_poisson_noise(): """Test Poisson random number generator """ pMean = 17 p = galsim.PoissonDeviate(testseed, mean=pMean) pResult = np.empty((10, 10)) p.generate(pResult) noise = galsim.DeviateNoise(p) # Test filling an image noise.rng.seed(testseed) testimage = galsim.ImageI(10, 10) testimage.addNoise(galsim.DeviateNoise(p)) np.testing.assert_array_equal( testimage.array, pResult, err_msg= 'Wrong poisson random number sequence generated when applied to image.' ) # The PoissonNoise version also subtracts off the mean value pn = galsim.PoissonNoise(galsim.BaseDeviate(testseed), sky_level=pMean) testimage.fill(0) testimage.addNoise(pn) np.testing.assert_array_equal( testimage.array, pResult - pMean, err_msg= 'Wrong poisson random number sequence generated using PoissonNoise') # Test filling a single-precision image pn.rng.seed(testseed) testimage = galsim.ImageF(10, 10) testimage.addNoise(pn) np.testing.assert_array_almost_equal( testimage.array, pResult - pMean, precisionF, err_msg= 'Wrong Poisson random number sequence generated when applied to ImageF.' ) # Test filling an image with Fortran ordering pn.rng.seed(testseed) testimage = galsim.ImageD(10, 10) testimage.addNoise(pn) np.testing.assert_array_almost_equal( testimage.array, pResult - pMean, err_msg="Wrong Poisson noise generated for Fortran-ordered Image") # Check PoissonNoise variance: np.testing.assert_almost_equal( pn.getVariance(), pMean, precision, err_msg="PoissonNoise getVariance returns wrong variance") np.testing.assert_almost_equal( pn.sky_level, pMean, precision, err_msg="PoissonNoise sky_level returns wrong value") # Check that the noise model really does produce this variance. big_im = galsim.Image(2048, 2048, dtype=float) big_im.addNoise(pn) var = np.var(big_im.array) print('variance = ', var) print('getVar = ', pn.getVariance()) np.testing.assert_almost_equal( var, pn.getVariance(), 1, err_msg='Realized variance for PoissonNoise did not match getVariance()' ) # Check that PoissonNoise adds to the image, not overwrites the image. gal = galsim.Exponential(half_light_radius=2.3, flux=0.3) # Note: in this case, flux/size^2 needs to be << sky_level or it will mess up the statistics. gal.drawImage(image=big_im) big_im.addNoise(pn) gal.withFlux(-0.3).drawImage(image=big_im, add_to_image=True) var = np.var(big_im.array) np.testing.assert_almost_equal( var, pn.getVariance(), 1, err_msg='PoissonNoise wrong when already an object drawn on the image') # Check withVariance pn = pn.withVariance(9.) np.testing.assert_almost_equal( pn.getVariance(), 9., precision, err_msg="PoissonNoise withVariance results in wrong variance") np.testing.assert_almost_equal( pn.sky_level, 9., precision, err_msg="PoissonNoise withVariance results in wrong sky_level") # Check withScaledVariance pn = pn.withScaledVariance(4.) np.testing.assert_almost_equal( pn.getVariance(), 36, precision, err_msg="PoissonNoise withScaledVariance results in wrong variance") np.testing.assert_almost_equal( pn.sky_level, 36., precision, err_msg="PoissonNoise withScaledVariance results in wrong sky_level") # Check arithmetic pn = pn.withVariance(0.5) pn2 = pn * 3 np.testing.assert_almost_equal( pn2.getVariance(), 1.5, precision, err_msg="PoissonNoise pn*3 results in wrong variance") np.testing.assert_almost_equal( pn.getVariance(), 0.5, precision, err_msg="PoissonNoise pn*3 results in wrong variance for original pn") pn2 = 5 * pn np.testing.assert_almost_equal( pn2.getVariance(), 2.5, precision, err_msg="PoissonNoise 5*pn results in wrong variance") np.testing.assert_almost_equal( pn.getVariance(), 0.5, precision, err_msg="PoissonNoise 5*pn results in wrong variance for original pn") pn2 = pn / 2 np.testing.assert_almost_equal( pn2.getVariance(), 0.25, precision, err_msg="PoissonNoise pn/2 results in wrong variance") np.testing.assert_almost_equal( pn.getVariance(), 0.5, precision, err_msg="PoissonNoise 5*pn results in wrong variance for original pn") pn *= 3 np.testing.assert_almost_equal( pn.getVariance(), 1.5, precision, err_msg="PoissonNoise pn*=3 results in wrong variance") pn /= 2 np.testing.assert_almost_equal( pn.getVariance(), 0.75, precision, err_msg="PoissonNoise pn/=2 results in wrong variance") # Check starting with PoissonNoise() pn = galsim.PoissonNoise() pn = pn.withVariance(9.) np.testing.assert_almost_equal( pn.getVariance(), 9., precision, err_msg="PoissonNoise().withVariance results in wrong variance") np.testing.assert_almost_equal( pn.sky_level, 9., precision, err_msg="PoissonNoise().withVariance results in wrong sky_level") pn = pn.withScaledVariance(4.) np.testing.assert_almost_equal( pn.getVariance(), 36, precision, err_msg="PoissonNoise().withScaledVariance results in wrong variance") np.testing.assert_almost_equal( pn.sky_level, 36., precision, err_msg="PoissonNoise().withScaledVariance results in wrong sky_level") # Check picklability do_pickle(pn, lambda x: (x.rng.serialize(), x.sky_level)) do_pickle(pn, drawNoise) do_pickle(pn) # Check copy, eq and ne pn = pn.withVariance(pMean) pn2 = galsim.PoissonNoise(pn.rng.duplicate(), pMean) pn3 = pn.copy() pn4 = pn.copy(rng=galsim.BaseDeviate(11)) pn5 = galsim.PoissonNoise(pn.rng, 2 * pMean) assert pn == pn2 assert pn == pn3 assert pn != pn4 assert pn != pn5 assert pn.rng.raw() == pn2.rng.raw() assert pn == pn2 assert pn == pn3 pn.rng.raw() assert pn != pn2 assert pn == pn3
def test_init(): """Test the basic initialization of a StarData object. """ # Use an odd-sized image, so image.true_center and image.center are the same thing. # Otherwise, u,v below will be half-integer values. size = 63 # Center the image at a non-trivial location to simulate this being a cutout from a larger # image. icen = 598 jcen = 109 # Use pixel scale = 1, so image_pos and focal pos are the same thing. image = galsim.Image(size,size, scale=1) field_pos = image.center # Update the bounds so the image is centered at icen, jcen. # Note: this also updates the wcs, so u,v at the center is still field_pos image.setCenter(icen, jcen) # Just draw something so it has non-trivial pixel values. galsim.Gaussian(sigma=5).drawImage(image) weight = galsim.ImageI(image.bounds, init_value=1) # all weights = 1 # To make below tests of weight pixel values useful, add the image to weight, so pixel # values are not all identical. image_pos = image.center properties = { 'ra' : 34.1234, 'dec' : -15.567, 'color_ri' : 0.5, 'color_iz' : -0.2, 'ccdnum' : 3 } stardata = piff.StarData(image, image_pos, weight=weight, properties=properties) # Test attributes np.testing.assert_array_equal(stardata.image.array, image.array) np.testing.assert_array_equal(stardata.weight.array, weight.array) np.testing.assert_equal(stardata.image_pos, image_pos) # Test properties access viw properties attribute or directly with [] for key, value in properties.items(): np.testing.assert_equal(stardata.properties[key], value) np.testing.assert_equal(stardata[key], value) # Test the automatically generated properties print('image_pos = ',image_pos) print('image.wcs = ',image.wcs) print('props = ',stardata.properties) for key, value in [ ('x',image_pos.x), ('y',image_pos.y), ('u',field_pos.x), ('v',field_pos.y) ]: np.testing.assert_equal(stardata.properties[key], value) np.testing.assert_equal(stardata[key], value) # Test access via getImage method: im, wt, pos = stardata.getImage() np.testing.assert_array_equal(im.array, image.array) np.testing.assert_array_equal(wt.array, weight.array) np.testing.assert_equal(pos, image_pos) # Test access via getDataVector method: # Note: This array() and then .T is like zip for Python lists. for data, wt, u, v in np.array(stardata.getDataVector()).T: # In this case, these should be integers, but round in case of numerical inaccuracy. iu = int(round(u)) jv = int(round(v)) # GalSim images access pixels as (x,y) np.testing.assert_equal(data, image(iu+icen,jv+jcen)) np.testing.assert_equal(wt, weight(iu+icen,jv+jcen)) # Numpy arrays access elements as [y,x] np.testing.assert_equal(data, image.array[jv+size//2, iu+size//2]) np.testing.assert_equal(wt, weight.array[jv+size//2, iu+size//2]) print("Passed basic initialization of StarData")
def load_images(stars, file_name, pointing=None, image_hdu=None, weight_hdu=None, badpix_hdu=None, sky=None, logger=None): """Load the image data into a list of Stars. We don't store the image data for Stars when we write them to a file, since that would take up a lot of space and is usually not desired. However, we do store the bounds in the original image where the star was cutout, so if you want to load back in the original data from the image file, you can do so with this function. :param stars: A list of Star instances. :param file_name: The file with the image data for these stars. :param pointing: The pointing direction to use. [default: None] :param image_hdu: The hdu to use for the main image. [default: None, which means either 0 or 1 as appropriate according to the compression.] :param weight_hdu: The hdu to use for the weight image. [default: None] :param badpix_hdu: The hdu to use for the bad pixel mask. [default: None] :param sky: Optional sky image or float value to subtract from the main image. [default: None] :param logger: A logger object for logging debug info. [default: None] :returns: a new list of Stars with the images information loaded. """ import galsim # TODO: This is largely copied from InputHandler.readImages. # This should probably be refactored a bit to avoid the duplicated code. logger = galsim.config.LoggerWrapper(logger) logger.info("Loading image information from file %s", file_name) image = galsim.fits.read(file_name, hdu=image_hdu) if sky is not None: image = image - sky # Either read in the weight image, or build a dummy one if weight_hdu is None: logger.debug("Making trivial (wt==1) weight image") weight = galsim.ImageI(image.bounds, init_value=1) else: logger.info("Reading weight image from hdu %d.", weight_hdu) weight = galsim.fits.read(file_name, hdu=weight_hdu) if np.all(weight.array == 0): logger.error( "According to the weight mask in %s, all pixels have zero weight!", file_name) # If requested, set wt=0 for any bad pixels if badpix_hdu is not None: logger.info("Reading badpix image from hdu %d.", badpix_hdu) badpix = galsim.fits.read(file_name, hdu=badpix_hdu) # The badpix image may be offset by 32768 from the true value. # If so, subtract it off. if np.any(badpix.array > 32767): logger.debug('min(badpix) = %s', np.min(badpix.array)) logger.debug('max(badpix) = %s', np.max(badpix.array)) logger.debug( "subtracting 32768 from all values in badpix image") badpix -= 32768 if np.any(badpix.array < -32767): logger.debug('min(badpix) = %s', np.min(badpix.array)) logger.debug('max(badpix) = %s', np.max(badpix.array)) logger.debug("adding 32768 to all values in badpix image") badpix += 32768 # Also, convert to int16, in case it isn't by default. badpix = galsim.ImageS(badpix) if np.all(badpix.array != 0): logger.error( "According to the bad pixel array in %s, all pixels are masked!", file_name) weight.array[badpix.array != 0] = 0 stars = [ Star(data=StarData(image=image[star.data.image.bounds], image_pos=star.data.image_pos, weight=weight[star.data.image.bounds], pointing=(pointing if pointing is not None else star.data.pointing), properties=star.data.properties, _xyuv_set=True), fit=star.fit) for star in stars ] return stars
import numpy as np # make a galsim Gaussian image, get moments imsize = 513 g_sigma = 4. pix_scale = 0.2 s_hlr = 2.0 g = galsim.Gaussian(sigma=g_sigma) g_im = galsim.ImageF(imsize, imsize) g_im = g.draw(image=g_im, dx=pix_scale) res = g_im.FindAdaptiveMom() print "Results for Gaussian without masking:" print "e1, e2, sigma: ",res.observed_shape.e1, res.observed_shape.e2, res.moments_sigma # now mask center pixel, make sure moments are same mask_im = galsim.ImageI(g_im.bounds)+1 cent_pix = int(round(0.5*(g_im.xmax+g_im.xmin))) mask_im.setValue(cent_pix, cent_pix, 0) res = g_im.FindAdaptiveMom(object_mask_image = mask_im) print "\nResults after masking central pixel at ",cent_pix print "e1, e2, sigma: ",res.observed_shape.e1, res.observed_shape.e2, res.moments_sigma # now mask central 9x9 pixels, make sure moments are same mask_im.setValue(cent_pix-1, cent_pix-1, 0) mask_im.setValue(cent_pix-1, cent_pix, 0) mask_im.setValue(cent_pix-1, cent_pix+1, 0) mask_im.setValue(cent_pix, cent_pix-1, 0) mask_im.setValue(cent_pix, cent_pix+1, 0) mask_im.setValue(cent_pix+1, cent_pix-1, 0) mask_im.setValue(cent_pix+1, cent_pix, 0) mask_im.setValue(cent_pix+1, cent_pix+1, 0)