def main(): stars = genStars(6.0) sources = genSources() shape = (300, 500) image1 = make_gaussian_sources_image(shape, stars) image2 = make_gaussian_sources_image(shape, sources) fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 8)) ax1.imshow(image1, origin='lower', interpolation='nearest') ax2.imshow(image2, origin='lower', interpolation='nearest') plt.show()
def create_dataset(shape=(512, 512), fwhm=12.5 * u.arcsec, pixsize=2 * u.arcsec, noise_level=1 * mJypb, n_sources=50, flux_min=1 * mJypb, flux_max=10 * mJypb, filename='fake_map.fits'): hits, uncertainty, mask = create_ancillary(shape, fwhm=None, noise_level=1 * mJypb) wcs = create_wcs(shape, pixsize=2 * u.arcsec, center=None) beam_std_pix = (fwhm / pixsize).decompose().value * gaussian_fwhm_to_sigma sources = create_fake_source(shape, wcs, beam_std_pix, flux_min=flux_min, flux_max=flux_max, n_sources=n_sources) sources_map = make_gaussian_sources_image( shape, sources) * sources['amplitude'].unit data = add_noise(sources_map, uncertainty) hdus = create_hdulist(data, hits, uncertainty, mask, wcs, sources, fwhm, noise_level) hdus.writeto(filename, overwrite=True)
def drawpic(filename, data, ra, dec, radius, number): sigma_psf = 2.5 sources = Table() size = np.size(data[:,0]) x = ra y = dec x1 = x - radius x2 = x + radius y1 = y - radius y2 = y + radius sources['x_mean'] = (data[:,0] - x1) / radius / 2. * 256. sources['y_mean'] = (data[:,1] - y1) / radius / 2. * 256. sources['x_stddev'] = sigma_psf*np.ones(size) sources['y_stddev'] = sources['x_stddev'] sources['theta'] = np.zeros(size) sources['flux'] = data[:,2] / np.min(data[:,2]) * 5000 tshape = (256, 256) image = (make_gaussian_sources_image(tshape, sources) + \ make_noise_image(tshape, distribution='poisson', mean=10., random_state=12) + \ make_noise_image(tshape, distribution='gaussian', mean=0., stddev=10., random_state=12)) plt.imshow(image, cmap='gray', extent = [x1, x2, y1, y2], interpolation='nearest', origin='lower') plt.xlabel('RA(°)') plt.ylabel('DEC(°)') for i in range(size): plt.text(data[i,0],data[i,1],np.str(i), fontsize=12, color = 'w') plt.savefig('output/fig/' + np.str(number) + '/'+str(number)+'.jpg')
def stars(image, number, max_counts=10000, gain=1, fwhm=4): """ Add some stars to the image. """ # Most of the code below is a direct copy/paste from # https://photutils.readthedocs.io/en/stable/_modules/photutils/datasets/make.html#make_100gaussians_image flux_range = [max_counts / 10, max_counts] y_max, x_max = image.shape xmean_range = [0.1 * x_max, 0.9 * x_max] ymean_range = [0.1 * y_max, 0.9 * y_max] xstddev_range = [fwhm, fwhm] ystddev_range = [fwhm, fwhm] params = dict([('amplitude', flux_range), ('x_mean', xmean_range), ('y_mean', ymean_range), ('x_stddev', xstddev_range), ('y_stddev', ystddev_range), ('theta', [0, 2 * np.pi])]) sources = make_random_gaussians_table(number, params, seed=12345) star_im = make_gaussian_sources_image(image.shape, sources) return star_im
def test_find_center_noise_bad_guess(): image = make_gaussian_sources_image(SHAPE, STARS) noise = make_noise_image(SHAPE, distribution='gaussian', mean=0, stddev=5) cen2 = spf.find_center(image + noise, [40, 50], max_iters=1) # Bad initial guess, noise, should take more than one try... with pytest.raises(AssertionError): np.testing.assert_allclose(cen2, [30, 40])
def test_find_center_noise_good_guess(): image = make_gaussian_sources_image(SHAPE, STARS) noise = make_noise_image(SHAPE, distribution='gaussian', mean=0, stddev=5) # Trying again with several iterations should work cen3 = spf.find_center(image + noise, [31, 41], max_iters=10) # Tolerance chosen based on some trial and error np.testing.assert_allclose(cen3, [30, 40], atol=0.02)
def test_find_center_no_star(): # No star anywhere near the original guess image = make_gaussian_sources_image(SHAPE, STARS) # Offset the mean from zero to avoid nan center noise = make_noise_image(SHAPE, distribution='gaussian', mean=1000, stddev=5, random_state=RANDOM_SEED) cen = spf.find_center(image + noise, [50, 200], max_iters=10) assert (np.abs(cen[0] - 50) > 1) and (np.abs(cen[1] - 200) > 1)
def test_best_center(self): table = Table() table["flux"] = [1000] table["x_mean"] = [299.5] table["y_mean"] = [299.5] arr = datasets.make_gaussian_sources_image((600, 600), table) result = find_best_center(arr, 3, [299.5, 299.5]) self.assertTrue(np.allclose((result[0], result[1]), [299.5, 299.5]))
def comp_img(comps, posns, img_shape, pix_size=0.6): src_tab = Table() src_tab["amplitude"] = [1] * len(comps) src_tab["x_mean"] = posns[0] src_tab["y_mean"] = posns[1] src_tab["x_stddev"] = comps["Maj"] / pix_size / 2.355 src_tab["y_stddev"] = comps["Min"] / pix_size / 2.355 src_tab["theta"] = 90 - comps["PA"] return make_gaussian_sources_image(img_shape, src_tab)
def add_gaussian_sources(self, within=(0, 1), cat_gen=pos_uniform, **kwargs): """Add gaussian sources into the map. Parameters ---------- within : tuple of 2 int force the sources within this relative range in the map cat_gen : function (`pos_uniform`|`pos_gridded`|`pos_list`|...) the function used to generate the pixel positions and flux of the sources (see Notes below) **kwargs any keyword arguments to be passed to the `cat_gen` function Notes ----- the `cat_gen` function is used to generate the list of x, y pixel positions and fluxes and must at least support the `shape=None, within=(0, 1), mask=None` arguments. """ shape = self.shape x_mean, y_mean, peak_flux = cat_gen(shape=shape, within=within, mask=self.mask, **kwargs) nsources = x_mean.shape[0] sources = Table(masked=True) sources["amplitude"] = peak_flux.to(self.unit * u.beam) sources["x_mean"] = x_mean sources["y_mean"] = y_mean sources["x_stddev"] = np.ones(nsources) * self.beam.sigma_pix.value sources["y_stddev"] = np.ones(nsources) * self.beam.sigma_pix.value sources["theta"] = np.zeros(nsources) # Crude check to be within the finite part of the map if self.mask is not None: within_coverage = ~self.mask[sources["y_mean"].astype(int), sources["x_mean"].astype(int)] sources = sources[within_coverage] # Gaussian sources... self._data += make_gaussian_sources_image(shape, sources) # Add an ID column sources.add_column(Column(np.arange(len(sources)), name="fake_id"), 0) # Transform pixel to world coordinates a, d = self.wcs.pixel_to_world_values(sources["x_mean"], sources["y_mean"]) sources.add_columns([Column(a * u.deg, name="ra"), Column(d * u.deg, name="dec")]) sources["_ra"] = sources["ra"] sources["_dec"] = sources["dec"] # Remove unnecessary columns sources.remove_columns(["x_mean", "y_mean", "x_stddev", "y_stddev", "theta"]) self.fake_sources = sources
def make_gaussian_im(x_size, y_size, fluxes=[100,1000,10000], x_pos=[500,250,750], y_pos=[300,80,460],std=[6,6,6]): shape = (x_size, y_size) table = Table() table['flux'] = fluxes table['x_mean'] = x_pos table['y_mean'] = y_pos table['x_stddev'] = std table['y_stddev'] = std image = make_gaussian_sources_image(shape, table) return image
def __init__(self, noise_dev=1.0): self.image_shape = [400, 500] data_file = get_pkg_data_filename('data/test_sources.csv') self._sources = Table.read(data_file) self.mean_noise = self.sources['amplitude'].max() / 100 self.noise_dev = noise_dev self._stars = make_gaussian_sources_image(self.image_shape, self.sources) self._noise = make_noise_image(self._stars.shape, mean=self.mean_noise, stddev=noise_dev)
def _simulate_image(self, exp_time: float, open_shutter: bool) -> NDArray[Any]: """Simulate an image. Args: exp_time: Exposure time in seconds. open_shutter: Whether the shutter is opened. Returns: numpy array with image. """ # get shape for image shape = (int(self.window[3]), int(self.window[2])) # create image with Gaussian noise for BIAS data = make_noise_image(shape, distribution="gaussian", mean=10, stddev=1.0) # non-zero exposure time? if exp_time > 0: # add DARK data += make_noise_image(shape, distribution="gaussian", mean=exp_time / 1e4, stddev=exp_time / 1e5) # add stars and stuff if open_shutter: # get solar altitude sun_alt = self.world.sun_alt # get mean flatfield counts flat_counts = 30000 / np.exp(-1.28 * (4.209 + sun_alt)) * exp_time # create flat data += make_noise_image(shape, distribution="gaussian", mean=flat_counts, stddev=flat_counts / 10.0) # get catalog with sources sources = self._get_sources_table(exp_time) # filter out all sources outside FoV sources = sources[ (sources["x_mean"] > 0) & (sources["x_mean"] < shape[1]) & (sources["y_mean"] > 0) & (sources["y_mean"] < shape[0]) ] # create image data += make_gaussian_sources_image(shape, sources) # saturate data[data > 65535] = 65535 # finished return cast(NDArray[Any], data).astype(np.uint16)
def make_noiseless_data(imager, imager_filter_name, star_table): """Generate noiseless data of star field. Based upon gunagala.make_noiseless_data, which currently doesn't do PSFs very well. Args: imager (gunagala.Imager): Instance of a gunagala imaging system. imager_filter_name (str): Filter to use for noiseless data. star_table (astropy.Table): Table of star coordinates and magnitudes. Returns: CCDData: Noiseless imaging data. """ ucac_filter_name = f'{ imager_filter_name.upper() }mag' electrons = np.zeros( (imager.wcs._naxis2, imager.wcs._naxis1)) * u.electron / (u.second * u.pixel) # Calculate observed sky background sky_rate = imager.sky_rate[imager_filter_name] if hasattr(imager.sky, 'relative_brightness'): pixel_coords = imager.get_pixel_coords() relative_sky = imager.sky.relative_brightness(pixel_coords, obs_time) sky_rate = sky_rate * relative_sky electrons = electrons + sky_rate # compute stellar fluxes and locations star_rate = imager.ABmag_to_rate( star_table[ucac_filter_name].data * u.ABmag, imager_filter_name) pixel_coords = imager.wcs.all_world2pix( star_table['_RAJ2000'].data * u.deg, star_table['_DEJ2000'].data * u.deg, 0) table = Table() table['amplitude'] = star_rate.value table['x_mean'] = pixel_coords[0] table['y_mean'] = pixel_coords[1] table['x_stddev'] = np.ones(len(pixel_coords[0])) # PSF width = 1 pixels table['y_stddev'] = np.ones(len(pixel_coords[0])) # PSF width = 1 pixels table['theta'] = np.zeros(len(pixel_coords[0])) star_data = make_gaussian_sources_image( (imager.wcs._naxis2, imager.wcs._naxis1), table) * star_rate.unit / u.pixel electrons = electrons + star_data noiseless = CCDData(electrons, wcs=imager.wcs) return (noiseless)
def test_radial_profile(): image = make_gaussian_sources_image(SHAPE, STARS) for row in STARS: cen = spf.find_center(image, (row['x_mean'], row['y_mean']), max_iters=10) print(row) r_ex, r_a, radprof = spf.radial_profile(image, cen) r_exs, r_as, radprofs = spf.radial_profile(image, cen, return_scaled=False) # Numerical value below is integral of input 2D gaussian, 2pi A sigma^2 expected_integral = 2 * np.pi * row['amplitude'] * row['x_stddev']**2 np.testing.assert_allclose(radprofs.sum(), expected_integral, atol=50)
def make_host_image(self, magnitude='default', noisy=True): table_target = Table() table_target['flux'] = np.random.poisson( [cnts_target.value, cnts_host.value]) gpos = [ np.random.uniform(high=image_shape[0]), np.random.uniform(high=image_shape[1]) ] table_target['x_mean'] = [ gpos[0] + np.random.uniform(low=target_dist_pixels.value, high=target_dist_pixels.value), gpos[0] ] table_target['y_mean'] = [ gpos[1] + np.random.uniform(low=target_dist_pixels.value, high=target_dist_pixels.value), gpos[1] ] table_target['x_stddev'] = [(x_stddev / arcsec_per_pixel).value, (galaxy_shape[0] / arcsec_per_pixel).value] table_target['y_stddev'] = [(y_stddev / arcsec_per_pixel).value, (galaxy_shape[1] / arcsec_per_pixel).value] table_target['theta'] = np.random.uniform(high=2 * np.pi, size=2) * u.radian table_target2 = table_target table_target2['flux'] = np.random.poisson( [cnts_target.value, cnts_host.value]) image_target = make_gaussian_sources_image(image_shape, Table(table_target[0]), oversample=psf_oversample) image_host = make_gaussian_sources_image(image_shape, Table(table_target[1]), oversample=psf_oversample) image_host2 = make_gaussian_sources_image(image_shape, Table(table_target2[1]), oversample=psf_oversample)
def add_fake_stars(image, expTime, number=N_STARS, max_counts=MAX_COUNTS, sky_counts=SKY_LEVEL, gain=GAIN): """ Adds fake stars to a dark image from the CCD. Used for testing while not on-telescope Input: - image The numpy array from the CCD. - number The number of stars to add - max_counts The max counts for the stars to have - sky_counts Counts to use for adding sky background - gain CCD gain - expTime The exposure time of the raw image, to scale star brightness Output: - fakeData A numpy array containing the fake star image """ # create sky background sky_im = np.random.poisson(sky_counts * gain, size=image.shape) / gain #flux_range = [max_counts/10, max_counts] # this the range for brightness, flux or counts flux_range = [ float(expTime) * (max_counts / 10), float(expTime) * (max_counts / 1) ] y_max, x_max = image.shape xmean_range = [0.1 * x_max, 0.9 * x_max] # this is where on the chip they land ymean_range = [0.1 * y_max, 0.9 * y_max] xstddev_range = [ 4, 4 ] # this is a proxy for gaussian width, FWHM, or focus I think. ystddev_range = [4, 4] params = dict([('amplitude', flux_range), ('x_mean', xmean_range), ('y_mean', ymean_range), ('x_stddev', xstddev_range), ('y_stddev', ystddev_range), ('theta', [0, 2 * np.pi])]) randInt = random.randint(11111, 99999) sources = make_random_gaussians_table(number, params, random_state=randInt) star_im = make_gaussian_sources_image(image.shape, sources) fakeData = image + sky_im + star_im return fakeData
def model2dG_build(self): """Function to fit a 2d-Gaussian to the built epsf """ # TODO once build_epsf is working smoothly in each class rewrite try: self.epsf epsf = self.epsf # the build_epsf except: epsf = self # use photutils 2d gaussian fit on the built epsf # TODO give option to restrict fit params, force xmean,ymean to be the ctr, constant to be zero gaussian = photutils.centroids.fit_2dgaussian(epsf.data) print(gaussian.param_names, gaussian.parameters) # unpack the parameters of fit constant, amplitude, x_mean, y_mean, x_stddev, y_stddev, theta = gaussian.parameters # Theta is in degrees, rotating the sigma_x,sigma_y ccw from +x # Put fit values into table # TODO the build_epsf is oversampled with respect to the fits image class data # ie the x_stddev, y_stddev are scaled by the oversampling # I think what makes the most sense is to rescale the build_epsf array, I'm not clear on how to do that table = Table() table['constant'] = [constant] table['amplitude'] = [amplitude] #table['flux'] = [flux] # if flux and amplitude flux is ignored table['x_mean'] = [x_mean] table['y_mean'] = [y_mean] table['x_stddev'] = x_stddev #[x_stddev/epsf.oversampling[0]] table['y_stddev'] = y_stddev #[y_stddev/epsf.oversampling[1]] # theta is a ccw rotation from +x in deg # ie the 2dgaussian grabs hold of a sigma_x sigma_y and then rotated table['theta'] = [theta] # get epsf of the model fit shape = epsf.shape modeled_epsf = make_gaussian_sources_image(shape, table) resid = modeled_epsf.data - epsf.data return gaussian, table, modeled_epsf
def test_find_center_no_noise_star_at_edge(): # Trying to put the star at the edge of the initial guess image = make_gaussian_sources_image(SHAPE, STARS) cen = spf.find_center(image, [45, 65], max_iters=10) np.testing.assert_allclose(cen, [30, 40])
def test_find_center_no_noise_good_guess(): image = make_gaussian_sources_image(SHAPE, STARS) # Good initial guess, no noise, should converge in one try cen1 = spf.find_center(image, (31, 41), max_iters=1) np.testing.assert_allclose(cen1, [30, 40])
def prepare_image(self): """Creates the image that will be fetched.""" # Default values exposure_params: Dict[str, Any] = dict( seed=None, shape=[ self.state["lr_x"] - self.state["ul_x"], self.state["lr_y"] - self.state["ul_y"], ], sources=False, noise=False, apply_poison_noise=False, ) if isinstance(self._exposure_params, list): if len(self._exposure_params) == 0: # If the simulation configuration doesn't include an "exposures" # section, just add some default noise. exposure_params["noise"] = { "distribution": "gaussian", "mean": 1000, "stddev": 20.0, } else: this_exposure = self._exposure_params[self._exposure_idx] # If str, file is the image to return if isinstance(this_exposure, str): data = astropy.io.fits.getdata(this_exposure) return data exposure_params.update(this_exposure) image = numpy.zeros(exposure_params["shape"][::-1], dtype="float32") if "seed" in exposure_params and exposure_params["seed"] is not None: numpy.random.seed(exposure_params["seed"]) if exposure_params["noise"]: image += make_noise_image(image.shape, **exposure_params["noise"]) if exposure_params["sources"]: if "source_table" in exposure_params["sources"]: source_table = astropy.table.Table.read( exposure_params["sources"]["source_table"]) else: n_sources = exposure_params["sources"]["n_sources"] if isinstance(n_sources, list): n_sources = numpy.random.randint(*n_sources) param_ranges = exposure_params["sources"]["param_ranges"] source_table = get_source_table(param_ranges, n_sources) source_image = make_gaussian_sources_image( image.shape, source_table=source_table, ) source_image *= self.state["exposure_time"] / 1000.0 image += source_image if exposure_params["apply_poison_noise"]: image = apply_poisson_noise(image, seed=exposure_params["seed"]) assert isinstance(image, numpy.ndarray) image[image > 2**16] = 2**16 - 1 self.image = image.astype("uint16")
def completeness_limit(self, line, StarFinder, threshold, distance_modulus, limit=0.8, max_sep=0.5, oversize=1., exclude_region=None, iterations=1, n_sources=500, plot=False, **kwargs): '''determine completness limit 1. Insert mock sources of different brightness 2. Run the source detection algorithm 3. Compare mock sources to detected sources and determine the faintest sources that have been detected. Parameters ---------- data : ndarray image with Returns ------- Table ''' daoargs = { k: kwargs.pop(k) for k in dict(kwargs) if k in inspect.signature(DAOStarFinder).parameters.keys() } for k, v in kwargs.items(): logger.warning(f'unused kwargs: {k}={v}') data = getattr(self, line).copy() err = getattr(self, f'{line}_err').copy() PSF = getattr(self, 'PSF') tshape = data.shape if not np.any(exclude_region): exclude_region = np.zeros(data.shape, dtype=bool) else: print( f'masking {np.sum(exclude_region)/np.prod(exclude_region.shape)*100:.2f} % of the image' ) #---------------------------------------------------------------- # craete mock data #---------------------------------------------------------------- try: wavelength = int(re.findall(r'\d{4}', line)[0]) PSF_correction = correct_PSF(wavelength) except: PSF_correction = 0 j = 0 while j < iterations: #for i in range(iterations): logger.info(f'iteration {j+1} of {iterations}') mock_sources = Table(data=np.zeros((n_sources, 7)), names=[ 'magnitude', 'flux', 'x_mean', 'y_mean', 'x_stddev', 'y_stddev', 'theta' ]) mock_sources['magnitude'] = sample_pnlf(n_sources, distance_modulus, 29.75) mock_sources['flux'] = 10**(-(mock_sources['magnitude'] + 13.74) / 2.5) * 1e20 # create a number of random points (more than we need because # some will fall in unobserved areas) f = 1.5 # create more points than we need while True: # number we create is f * number of sources we want / # (observed area / total area ) N = f * n_sources / (np.sum(~np.isnan(PSF)) / np.prod(self.shape)) indices = np.random.uniform((0, 0), self.shape, (int(N), 2)) x_mean = indices[:, 1] y_mean = indices[:, 0] PSF_arr = np.array( [PSF[int(y), int(x)] for x, y in zip(x_mean, y_mean)]) in_frame = ~np.isnan(PSF_arr) x_mean = x_mean[in_frame] y_mean = y_mean[in_frame] PSF_arr = PSF_arr[in_frame] if len(x_mean) > n_sources: x_mean = x_mean[:n_sources] y_mean = y_mean[:n_sources] PSF_arr = PSF_arr[:n_sources] break else: # it might happen that more points fall in unobserved # areas. In this case we have to repeat f *= 1.1 mock_sources['x_mean'], mock_sources['y_mean'] = x_mean, y_mean # get PSF size at the generated position mock_sources['x_stddev'] = ( PSF_arr - PSF_correction) * gaussian_fwhm_to_sigma * oversize mock_sources['y_stddev'] = mock_sources['x_stddev'] mock_sources['amplitude'] = mock_sources['flux'] / ( mock_sources['x_stddev'] * np.sqrt(2 * np.pi)) logger.info(f'{len(mock_sources)} mock sources created') if plot: fig = plt.figure(figsize=(6, 6)) ax = fig.add_subplot(111, projection=self.wcs) norm = simple_norm(data, 'linear', clip=False, max_percent=95) ax.imshow(data, norm=norm, cmap=plt.cm.Blues_r, origin='lower') len(mock_sources) positions = np.transpose( [mock_sources['x_mean'], mock_sources['y_mean']]) apertures = CircularAperture(positions, r=6) ax.scatter(mock_sources['x_mean'], mock_sources['y_mean'], color='tab:red') #apertures.plot(color='tab:red',lw=.2, alpha=1,ax=ax) plt.show() mock_img = make_gaussian_sources_image(tshape, mock_sources) mock_img += data logger.info('mock sources inserted into image') #---------------------------------------------------------------- # detection run #---------------------------------------------------------------- for fwhm in np.unique(PSF[~np.isnan(PSF)]): # we create a mask for the current pointing (must be inverted) psf_mask = (PSF == fwhm) #& (~np.isnan(PSF)) source_mask = make_source_mask( data, nsigma=2, npixels=5, dilate_size=int( 3 * fwhm)) | ~psf_mask mean, median, std = sigma_clipped_stats(data, sigma=3.0, maxiters=5, mask=source_mask) # initialize and run StarFinder (DAOPHOT or IRAF) finder = StarFinder(fwhm=(fwhm - PSF_correction) * oversize, threshold=threshold * std, **daoargs) peaks_part = finder(mock_img, mask=(~psf_mask | exclude_region)) if peaks_part: #logger.info(f'fwhm={fwhm:.3f}: {len(peaks_part)} sources found') if 'peak_tbl' in locals(): peaks_part['id'] += np.amax(peak_tbl['id'], initial=0) peak_tbl = vstack([peak_tbl, peaks_part]) else: peak_tbl = peaks_part else: logger.info(f'fwhm={fwhm:>7.3f}: no sources found') if 'peak_tbl' in locals(): logger.info(f'{len(peak_tbl)} sources found') else: logger.warning('no sources found') continue #---------------------------------------------------------------- # compare detected sources to known mock stars #---------------------------------------------------------------- logger.info(f'compare detected sources to injected sources') idx, sep = match_catalogues(mock_sources[['x_mean', 'y_mean']], peak_tbl[['xcentroid', 'ycentroid']]) mock_sources['sep'] = sep mock_sources['peak'] = peak_tbl[idx]['peak'] if 'out' in locals(): out = vstack([out, mock_sources]) else: out = mock_sources del peak_tbl j += 1 #---------------------------------------------------------------- # create the histogram #---------------------------------------------------------------- filename = basedir / 'reports' / self.name / f'{self.name}_completness.pdf' plot_completeness_limit(out, max_sep=max_sep, limit=limit, filename=filename) #---------------------------------------------------------------- for col in out.colnames: out[col].info.format = '%.8g' return out
from astropy.table import Table from photutils.datasets import (make_random_gaussians_table, make_noise_image, make_gaussian_sources_image) sigma_psf = 2.0 sources = Table() sources['flux'] = [700, 800, 700, 800] sources['x_mean'] = [12, 17, 12, 17] sources['y_mean'] = [15, 15, 20, 20] sources['x_stddev'] = sigma_psf * np.ones(4) sources['y_stddev'] = sources['x_stddev'] sources['theta'] = [0, 0, 0, 0] sources['id'] = [1, 2, 3, 4] tshape = (32, 32) image = ( make_gaussian_sources_image(tshape, sources) + make_noise_image(tshape, distribution='poisson', mean=6., random_state=1) + make_noise_image( tshape, distribution='gaussian', mean=0., stddev=2., random_state=1)) from matplotlib import rcParams rcParams['font.size'] = 13 import matplotlib.pyplot as plt plt.imshow(image, cmap='viridis', aspect=1, interpolation='nearest', origin='lower') plt.title('Simulated data')
def fake_image(n_sources=100, shape=[512, 512], amplitude_r=[0, 20000], std_dev=[0, 7], random_state=666, noise={ 'type': None, 'mean': None, 'stddev': None }): """Creates fake image with Gaussian sources. Creates a fake image with gaussian sources, whose parameters can be adjusted based on the following arguments. A background with spatial fluctuations at various scales is created seperately. This is achived by first creating the desired 2D power spectrum, which is a radial power law in Fourier space. According to litrature, the index of ISM power law distribution is -2.9. Taking the inverse FFT of this p_law array gives a background with the desired levels of spatial fluctuations in real space. Args: n_sources(int): Number of sources shape(2-tuple): Dimensions of the image amplitude_r(list): Range of amplitudes of sources std_dev(list): Range of standard deviations of sources random_state(int): Seed for random number generator noise(dictionary): Parameters for noise (i) type: Gaussian or Poisson (ii) mean: Mean value of noise (iii) stddev: Standard deviation of gaussian noise """ param_ranges = [('amplitude', [amplitude_r[0], amplitude_r[1]]), ('x_mean', [0, shape[1]]), ('y_mean', [0, shape[0]]), ('x_stddev', [std_dev[0], std_dev[1]]), ('y_stddev', [std_dev[0], std_dev[1]]), ('theta', [0, np.pi])] param_ranges = OrderedDict(param_ranges) sources = make_random_models_table(n_sources, param_ranges, random_state=random_state) if noise['type'] is None: sources = make_gaussian_sources_image(shape, sources) else: sources = sources + make_noise_image(shape, type=noise['type'], mean=noise['mean'], stddev=noise['stddev']) # CREATING BACKGROUNG (ISM) # The objective is to create a background with different levels of # spatial fluctuations built in. This is achived by first creating the # desired 2D power spectrum, which is a radial power law in Fourier space. # According to litrature, the index of ISM power law distribution is -2.9 # Taking the inverse FFT of this p_law array gives a background with the # desired levels of spatial fluctuations in real space. p_law = np.zeros(shape, dtype=float) y, x = np.indices(p_law.shape) center = np.array([(y.max() - y.min()) / 2.0, (x.max() - x.min()) / 2.0]) r = np.hypot(x - center[1], y - center[0]) r_ind = r.astype(int) r_max = r.max().astype(int) a = np.arange(0.1, r_max + 1.1, 1) # These values control size of clouds b = 10**11 * a**(-2.9) # This controls magnitude of background for i in range(0, r_max + 1): p_law[r_ind > i] = b[i] magnitude = np.sqrt(p_law) phase = 2 * np.pi * np.random.randn(shape[0], shape[1]) FFT = magnitude * np.exp(1j * phase) background = np.abs((fftpack.ifft2(fftpack.fftshift(FFT)))) sim_sky = sources + background return sources, background, sim_sky
def make_gaia(coords, image=os.path.join('.', 'synthetic_gaia.fits'), epoch="J2020.5"): """ Creates a synthetic image from GAIA DR2 photometry along the specified coordinates. Returns the synthetic image in fits format. Parameters: coords: Coordinates of centre of image. image: File name of image. Default is synthetic_gaia.fits epoch: Epoch to translate image. Default is J2020.5 """ ##### ##### Define GMOS parameters ##### GMOS FoV is 330 arcsec ##### GMOS slit length is 330 arcsec ##### GMOS fwhm chosen in 0.3 arcsec ##### Image created is 390 arcsec, to accomadte additional overlays ##### GMOS read noise is 3.96 (e-/ADU) ##### GMOS gain is 1.829 (e- rms) ##### gmosfov = 390 * u.arcsec fwhm = 0.3 * u.arcsec shape = (1300, 1300) zeroarr = np.zeros(shape) width = 390 * u.arcsec height = 390 * u.arcsec ##### Read coordinates and epoch coords = coords epoch = Time(epoch, format='jyear_str') ##### Query GAIA DR2 using astroquery TAP+ ##### Synchronous query, limit of 2000 rows print('Searching GAIA TAP+ for stars over the GMOS FoV...') gaiasearch = Gaia.cone_search_async(coordinate=coords, radius=gmosfov, verbose=False) searchresults = gaiasearch.get_results() nstars = len(searchresults) print(nstars, 'stars recovered from GAIA TAP+ query over the GMOS FoV') if nstars == 2000: warnings.warn( 'Asynchronous TAP+ service query limit reached, image is incomplete.' ) fitshdr = mkfitshdr(coords, zeroarr, image, epoch) hdr = fitshdr[0] hdu = fitshdr[1] hdul = fitshdr[2] w = fitshdr[3] c = SkyCoord(ra=searchresults['ra'], dec=searchresults['dec'], pm_ra_cosdec=searchresults['pmra'], pm_dec=searchresults['pmdec'], obstime=Time(searchresults['ref_epoch'], format='decimalyear')) cnew = c ###### in the current implementation, transformation of the image to the epoch is not applied ###### cnew = c.apply_space_motion(new_obstime=Time(epoch)) star_coords = w.wcs_world2pix([cnew.ra.deg], [cnew.dec.deg], 0) xvalue = star_coords[0].ravel() yvalue = star_coords[1].ravel() flux = (searchresults['phot_g_mean_flux'] * u.mag).value ##### Error values cerr = SkyCoord(ra=searchresults['ra_error'], dec=searchresults['dec_error']) xerrvalue = cerr.ra.value yerrvalue = cerr.dec.value xerrmask = np.nan_to_num(xerrvalue, nan=0.0, posinf=0.0, neginf=0.0) yerrmask = np.nan_to_num(yerrvalue, nan=0.0, posinf=0.0, neginf=0.0) xerrmask[xerrmask < 1.0] = 1.0 yerrmask[yerrmask < 1.0] = 1.0 ##### Error values less than 1.0 lead to incorrect values in photutils. Error values of 1.0 lead to no significiant differences in images compared to no error. ##### Create Table of results t = Table([flux, xvalue, yvalue, xerrmask, yerrmask], names=('flux', 'x_mean', 'y_mean', 'x_stddev', 'y_stddev'), dtype=('i8', 'i8', 'i8', 'i8', 'i8')) print('Table of stars is being generated as an image') zeroarr = make_gaussian_sources_image(shape, t) ##### Read noise read_noise = np.random.normal(scale=(3.92 / 1.829), size=shape) noise_image = make_noise_image(shape, distribution='poisson', mean=0.5) ##### synth_image = zeroarr + noise_image ##### ###### read_noise is disabled hdu = fits.PrimaryHDU(synth_image, header=hdr) hdul = fits.HDUList([hdu]) hdul.writeto(image, overwrite=True) print('Synthetic GAIA DR2 image is saved as', image) return image
def generate_fits(tmpdir_factory): tmpdir = tmpdir_factory.mktemp("nm_map") filename = str(tmpdir.join("map.fits")) # Larger map to perform check_SNR np.random.seed(0) shape = (256, 256) pixsize = 1 / 3 * u.deg peak_flux = 1 * u.Jy noise_level = 0.1 * u.Jy / u.beam fwhm = 1 * u.deg nsources = 1 wcs = WCS() wcs.wcs.crpix = np.asarray(shape) / 2 - 0.5 # Center of pixel wcs.wcs.cdelt = np.asarray([-1, 1]) * pixsize wcs.wcs.ctype = ("RA---TAN", "DEC--TAN") xx, yy = np.indices(shape) mask = np.sqrt((xx - (shape[1] - 1) / 2)**2 + (yy - (shape[0] - 1) / 2)**2) > shape[0] / 2 sources = Table(masked=True) sources["amplitude"] = np.ones(nsources) * peak_flux sources["x_mean"] = [shape[1] / 2] sources["y_mean"] = [shape[0] / 2] beam_std_pix = (fwhm / pixsize).decompose().value * gaussian_fwhm_to_sigma sources["x_stddev"] = np.ones(nsources) * beam_std_pix sources["y_stddev"] = np.ones(nsources) * beam_std_pix sources["theta"] = np.zeros(nsources) data = make_gaussian_sources_image(shape, sources) hits = np.ones(shape=shape, dtype=np.float) uncertainty = np.ones(shape, dtype=np.float) * noise_level.to( u.Jy / u.beam).value data += np.random.normal(loc=0, scale=1, size=shape) * uncertainty data[mask] = np.nan hits[mask] = 0 uncertainty[mask] = 0 header = wcs.to_header() header["UNIT"] = "Jy / beam", "Fake Unit" primary_header = fits.header.Header() primary_header["f_sampli"] = 10.0, "Fake the f_sampli keyword" primary_header["FWHM_260"] = fwhm.to( u.arcsec).value, "[arcsec] Fake the FWHM_260 keyword" primary_header["FWHM_150"] = fwhm.to( u.arcsec).value, "[arcsec] Fake the FWHM_150 keyword" primary_header["nsources"] = 1, "Number of fake sources" primary_header["noise"] = noise_level.to( u.Jy / u.beam).value, "[Jy/beam] noise level per map" primary = fits.hdu.PrimaryHDU(header=primary_header) hdus = fits.hdu.HDUList(hdus=[primary]) for band in ["1mm", "2mm"]: hdus.append( fits.hdu.ImageHDU(data, header=header, name="Brightness_{}".format(band))) hdus.append( fits.hdu.ImageHDU(uncertainty, header=header, name="Stddev_{}".format(band))) hdus.append( fits.hdu.ImageHDU(hits, header=header, name="Nhits_{}".format(band))) hdus.append(fits.hdu.BinTableHDU(sources, name="fake_sources")) hdus.writeto(filename, overwrite=True) return filename
def generate_nikamaps( tmpdir_factory, shape=(61, 61), pixsize=1 / 3 * u.deg, noise_level=1 * Jybeam, nmaps=10, nsources=5, fwhm=1 * u.deg, nrots=4, ): # Generate several maps with sources and noise... only one band... tmpdir = tmpdir_factory.mktemp("nm_maps") wcs = WCS() wcs.wcs.crpix = np.asarray(shape) / 2 - 0.5 # Center of pixel wcs.wcs.cdelt = np.asarray([-1, 1]) * pixsize wcs.wcs.ctype = ("RA---TAN", "DEC--TAN") # Fake sources for all maps np.random.seed(0) sources = Table(masked=True) sources["amplitude"] = np.random.uniform(1, 10, size=nsources) * u.Jy sources["x_mean"] = np.random.uniform(1 / 4, 3 / 4, size=nsources) * shape[1] sources["y_mean"] = np.random.uniform(1 / 4, 3 / 4, size=nsources) * shape[0] beam_std_pix = (fwhm / pixsize).decompose().value * gaussian_fwhm_to_sigma sources["x_stddev"] = np.ones(nsources) * beam_std_pix sources["y_stddev"] = np.ones(nsources) * beam_std_pix sources["theta"] = np.zeros(nsources) data_sources = make_gaussian_sources_image(shape, sources) * u.Jy / u.beam a, d = wcs.wcs_pix2world(sources["x_mean"], sources["y_mean"], 0) sources.add_columns( [Column(a * u.deg, name="ra"), Column(d * u.deg, name="dec")]) sources.remove_columns( ["x_mean", "y_mean", "x_stddev", "y_stddev", "theta"]) sources.sort("amplitude") sources.reverse() sources.add_column(Column(np.arange(len(sources)), name="ID"), 0) # Elliptical gaussian mask def elliptical_mask_rot(shape, sigma, theta, limit): xx, yy = np.indices(shape) xx_arr = xx - (shape[1] - 1) / 2 yy_arr = yy - (shape[0] - 1) / 2 c, s = np.cos(theta), np.sin(theta) rot = np.array(((c, -s), (s, c))) xx_arr, yy_arr = np.dot(rot, [xx_arr.flatten(), yy_arr.flatten()]) xx_arr = (xx_arr.reshape(shape) / (2 * sigma[1]**2))**2 yy_arr = (yy_arr.reshape(shape) / (2 * sigma[0]**2))**2 mask = np.sqrt(xx_arr + yy_arr) > limit return mask # mask = mask_rot(shape, (np.sqrt(0.5), np.sqrt(0.5)), 0, (shape[0] - 1) / 2) primary_header = fits.header.Header() primary_header["f_sampli"] = 10.0, "Fake the f_sampli keyword" primary_header["FWHM_260"] = ( fwhm.to(u.arcsec).value, "[arcsec] Fake the FWHM_260 keyword", ) primary_header["FWHM_150"] = ( fwhm.to(u.arcsec).value, "[arcsec] Fake the FWHM_150 keyword", ) primary_header["nsources"] = nsources, "Number of fake sources" primary_header["pixsize"] = pixsize.to(u.deg).value, "[deg] pixel size" primary_header["nmaps"] = nmaps, "number of maps produced" primary_header["nrots"] = nmaps, "number of rotations" primary_header["shape0"] = shape[0], "[0] of map shape" primary_header["shape1"] = shape[1], "[1] of map shape" primary_header["noise"] = ( noise_level.to(u.Jy / u.beam).value, "[Jy/beam] noise level per map", ) primary = fits.hdu.PrimaryHDU(header=primary_header) filenames = [] for i_map in range(nmaps): # Rotated asymetric mask mask = elliptical_mask_rot( shape, (np.sqrt(1 / 2), np.sqrt(2 / 5)), i_map * np.pi / nrots, (shape[0] - 1) / 2, ) filename = str(tmpdir.join("map_{}.fits".format(i_map))) hits = np.ones(shape=shape, dtype=np.float) uncertainty = np.ones(shape=shape, dtype=np.float) * noise_level data = np.random.normal(loc=0, scale=1, size=shape) * uncertainty data += data_sources data[mask] = 0 hits[mask] = 0 uncertainty[mask] = 0 header = wcs.to_header() header["UNIT"] = "Jy / beam", "Fake Unit" hdus = fits.hdu.HDUList(hdus=[primary]) for band in ["1mm", "2mm"]: hdus.append( fits.hdu.ImageHDU(data.value, header=header, name="Brightness_{}".format(band))) hdus.append( fits.hdu.ImageHDU(uncertainty.value, header=header, name="Stddev_{}".format(band))) hdus.append( fits.hdu.ImageHDU(hits, header=header, name="Nhits_{}".format(band))) hdus.append(fits.hdu.BinTableHDU(sources, name="fake_sources")) hdus.writeto(filename, overwrite=True) filenames.append(filename) return filenames
from astropy.table import Table from photutils.datasets import make_gaussian_sources_image from photutils.datasets import make_noise_image from astropy.io import fits nstar = 400 sigma_psf = 2.0 shape = (500, 500) noise_mean = 5.0 np.random.seed(1000) # make a table of Gaussian sources table = Table() table['flux'] = 1000 - 1000 * np.random.power(2.35, size=nstar) table['x_mean'] = np.random.randint(0, high=shape[1] - 1, size=nstar) table['y_mean'] = np.random.randint(0, high=shape[0] - 1, size=nstar) table['x_stddev'] = sigma_psf * np.ones(nstar) table['y_stddev'] = table['x_stddev'] table['theta'] = np.radians(np.zeros(nstar)) # make an image of the sources with Poisson noise image1 = make_gaussian_sources_image(shape, table) image2 = image1 + make_noise_image( shape, distribution='poisson', mean=noise_mean) fig = plt.figure(figsize=(10, 10)) plt.imshow(image2, origin='lower', interpolation='nearest', cmap='gray') plt.savefig('image.jpg') hdu = fits.PrimaryHDU(image2) hdu.writeto('image.fits', overwrite=True)
def test_detection(StarFinder_Algorithm, sigma_psf, amplitude, PSF_size=1): '''create an image with mock sources and try to detect them Parameters ---------- StarFinder_Algorithm: Class to detect stars sigma_psf: standard deviation of the PSF of the mock sources amplitude: amplitude of the mock sources PSF_size: The StarFinder_Algorithm need to know the sigma of the sources they try to detect. This parameter changes the provided size compared to the sigma of the mock sources. ''' # create mock data n_sources = 20 tshape = (256, 256) param_ranges = OrderedDict([('amplitude', [amplitude, amplitude * 1.2]), ('x_mean', [0, tshape[0]]), ('y_mean', [0, tshape[1]]), ('x_stddev', [sigma_psf, sigma_psf]), ('y_stddev', [sigma_psf, sigma_psf]), ('theta', [0, 0])]) sources = make_random_gaussians_table(n_sources, param_ranges, random_state=1234) image = (make_gaussian_sources_image(tshape, sources) + make_noise_image( tshape, type='poisson', mean=6., random_state=1) + make_noise_image( tshape, type='gaussian', mean=0., stddev=2., random_state=34234)) fwhm = gaussian_sigma_to_fwhm * sigma_psf mean, median, std = sigma_clipped_stats(image, sigma=3.0) StarFinder = StarFinder_Algorithm(fwhm=fwhm * PSF_size, threshold=3. * std, sharplo=0.1, sharphi=1.0, roundlo=-.2, roundhi=.2) sources_mock = StarFinder(image) # for consistent table output for col in sources_mock.colnames: sources_mock[col].info.format = '%.8g' string = str(StarFinder_Algorithm).split( '.')[-1][:-2] + f' sig={sigma_psf} A={amplitude}' print(f'{string}: {len(sources_mock):} of {n_sources} sources found') positions = np.transpose( [sources_mock['xcentroid'], sources_mock['ycentroid']]) apertures = CircularAperture(positions, r=fwhm) return image, apertures, sources, string