def frame_filter_lowpass(array, mode='gauss', median_size=5, fwhm_size=5, gauss_mode='conv'): """ Low-pass filtering of input frame depending on parameter *mode*. Parameters ---------- array : array_like Input array, 2d frame. mode : {'median', 'gauss'}, str optional Type of low-pass filtering. median_size : int, optional Size of the median box for filtering the low-pass median filter. fwhm_size : int, optional Size of the Gaussian kernel for the low-pass Gaussian filter. gauss_mode : {'conv', 'convfft'}, str optional 'conv' uses the multidimensional gaussian filter from scipy.ndimage and 'convfft' uses the fft convolution with a 2d Gaussian kernel. Returns ------- filtered : array_like Low-pass filtered image. """ if array.ndim != 2: raise TypeError('Input array is not a frame or 2d array.') if mode == 'median': # creating the low_pass filtered (median) image filtered = median_filter(array, int(median_size), mode='nearest') elif mode == 'gauss': # 2d Gaussian filter sigma = fwhm_size * gaussian_fwhm_to_sigma if gauss_mode == 'conv': filtered = gaussian_filter(array, sigma=sigma, order=0, mode='nearest') elif gauss_mode == 'convfft': # FFT Convolution with a 2d gaussian kernel created with Astropy. filtered = convolve_fft(array, Gaussian2DKernel(stddev=sigma)) else: raise TypeError('2d Gaussian filter mode not recognized') else: raise TypeError('Low-pass filter mode not recognized') return filtered
def test_convolve_vs_smooth(): axes = [ MapAxis(np.logspace(0.0, 3.0, 3), interp="log"), MapAxis(np.logspace(1.0, 3.0, 4), interp="lin"), ] binsz = 0.05 * u.deg m = WcsNDMap.create(binsz=binsz, width=1.05 * u.deg, axes=axes) m.data[:, :, 10, 10] = 1.0 desired = m.smooth(kernel="gauss", width=0.5 * u.deg, mode="constant") gauss = Gaussian2DKernel(10).array actual = m.convolve(kernel=gauss) assert_allclose(actual.data, desired.data, rtol=1e-3)
def test_compute_ts_map_downsampled(input_maps): """Minimal test of compute_ts_image""" kernel = Gaussian2DKernel(2.5) ts_estimator = TSMapEstimator(method="root brentq", error_method="conf", ul_method="conf") result = ts_estimator.run(input_maps, kernel=kernel, downsampling_factor=2) assert_allclose(result["ts"].data[99, 99], 1675.28, rtol=1e-2) assert_allclose(result["niter"].data[99, 99], 7) assert_allclose(result["flux"].data[99, 99], 1.02e-09, rtol=1e-2) assert_allclose(result["flux_err"].data[99, 99], 3.84e-11, rtol=1e-2) assert_allclose(result["flux_ul"].data[99, 99], 1.10e-09, rtol=1e-2)
def gaussian_kernel(data_shape, sigma, norm='max'): r"""Gaussian kernel This method produces a Gaussian kerenal of a specified size and dispersion Parameters ---------- data_shape : tuple Desiered shape of the kernel sigma : float Standard deviation of the kernel norm : str {'max', 'sum', 'none'}, optional Normalisation of the kerenl (options are 'max', 'sum' or 'none') Returns ------- np.ndarray kernel Examples -------- >>> from modopt.math.stats import gaussian_kernel >>> gaussian_kernel((3, 3), 1) array([[ 0.36787944, 0.60653066, 0.36787944], [ 0.60653066, 1. , 0.60653066], [ 0.36787944, 0.60653066, 0.36787944]]) >>> gaussian_kernel((3, 3), 1, norm='sum') array([[ 0.07511361, 0.1238414 , 0.07511361], [ 0.1238414 , 0.20417996, 0.1238414 ], [ 0.07511361, 0.1238414 , 0.07511361]]) """ if not import_astropy: # pragma: no cover raise ImportError('Astropy package not found.') if norm not in ('max', 'sum', 'none'): raise ValueError('Invalid norm, options are "max", "sum" or "none".') kernel = np.array(Gaussian2DKernel(sigma, x_size=data_shape[1], y_size=data_shape[0])) if norm == 'max': return kernel / np.max(kernel) elif norm == 'sum': return kernel / np.sum(kernel) elif norm == 'none': return kernel
def _create_psf(self, **kwargs): """Set up psf model using `astropy.convolution`. """ # Read psf info import json psf_data = json.load(open(self.psf_file)) # Convert sigma and amplitude sigma_1 = gaussian_fwhm_to_sigma * psf_data['psf1']['fwhm'] sigma_2 = gaussian_fwhm_to_sigma * psf_data['psf2']['fwhm'] sigma_3 = gaussian_fwhm_to_sigma * psf_data['psf3']['fwhm'] ampl_1 = psf_data['psf1']['ampl'] * 2 * np.pi * sigma_1 ** 2 ampl_2 = psf_data['psf2']['ampl'] * 2 * np.pi * sigma_2 ** 2 ampl_3 = psf_data['psf3']['ampl'] * 2 * np.pi * sigma_3 ** 2 # Setup kernels from astropy.convolution import Gaussian2DKernel gauss_1 = Gaussian2DKernel(sigma_1, **kwargs) gauss_2 = Gaussian2DKernel(sigma_2, **kwargs) gauss_3 = Gaussian2DKernel(sigma_3, **kwargs) psf = gauss_1 * ampl_1 + gauss_2 * ampl_2 + gauss_3 * ampl_3 psf.normalize() return psf
def get_kernel(self, fov): # called by .apply_to() from the base PSF class pixel_scale = fov.header["CDELT1"] * u.deg.to(u.arcsec) pixel_scale = utils.quantify(pixel_scale, u.arcsec) wave = fov.wavelength ### add in the conversion to fwhm from seeing and wavelength here fwhm = self.meta["fwhm"] * u.arcsec / pixel_scale sigma = fwhm.value / 2.35 kernel = Gaussian2DKernel(sigma, mode="center").array return kernel
def smooth_image(model: Image, width=1.0): """ Smooth an image with a kernel """ assert type(model) == Image kernel = Gaussian2DKernel(width) cmodel = create_empty_image_like(model) cmodel.data[..., :, :] = convolve(model.data[0, 0, :, :], kernel, normalize_kernel=False) if type(kernel) is Gaussian2DKernel: cmodel.data *= 2 * numpy.pi * 1.5 ** 2 return cmodel
def convolve_spatial(self, fwhmx=None, fwhmy=None): if fwhmx is None and fwhmy is None: print("No convolution to be done with both FWHM as None") return elif fwhmx <= 0. and fwhmy <= 0.: print("No convolution to be done with FWHM <= 0") return if fwhmx > self.fwhmx: conv_fwhmx = np.sqrt(fwhmx**2 - self.fwhmx**2) reached_fwhmx = fwhmx else: print("Warning: objective FWHM_X smaller than present value") conv_fwhmx = 0. reached_fwhmx = self.fwhmx if fwhmy > self.fwhmy: conv_fwhmy = np.sqrt(fwhmy**2 - self.fwhmy**2) reached_fwhmy = fwhmy else: print("Warning: objective FWHM_Y smaller than present value") conv_fwhmy = 0. reached_fwhmy = self.fwhmy closvd = copy.deepcopy(self) print("LOSVD FWHM: XLOS[{0}] YLOS[{1}]".format(self.fwhmx, self.fwhmy)) print("Starting the Convolution to reach FWHM: XCLOS[{0}] YCLOS[{1}]". format(reached_fwhmx, reached_fwhmy)) print( "Convolving with quadratic residuals FWHM: DX[{0}] DY[{1}]".format( conv_fwhmx, conv_fwhmy)) # Building the kernel kernel = Gaussian2DKernel( x_stddev=conv_fwhmx * gaussian_fwhm_to_sigma / self.stepx, y_stddev=conv_fwhmy * gaussian_fwhm_to_sigma / self.stepy) # Doing the convolution per image slice for k in range(self.losvd.shape[2]): closvd.losvd[:, :, k] = convolve(self.losvd[:, :, k], kernel) if self.err_losvd[0, 0] is not None: for k in range(self.losvd.shape[2]): closvd.err_losvd[:, :, k] = convolve(self.err_losvd[:, :, k], kernel) closvd.fwhmx = reached_fwhmx closvd.fwhmy = reached_fwhmy # Returning the result return closvd
def make_HSC_detect_mask(bin_msk, img, objects, segmap, r=10.0, radius=1.5, threshold=0.01): '''Make HSC detection and bright star mask, based on HSC binary mask flags. Parameters: ----------- bin_msk: 2-D np.array, can be loaded from HSC image cutouts objects: table, returned from sep.extract_obj segmap: 2-D np.array, returned from sep.extract_obj r: float, blow-up parameter radius: float, convolution radius threshold: float, threshold of making mask after convolution Returns: ----------- HSC_detect_mask: 2-D boolean np.array See also: ----------------- convert_HSC_binary_mask(bin_msk) ''' import sep TDmask = convert_HSC_binary_mask(bin_msk) cen_mask = np.zeros(bin_msk.shape, dtype=np.bool) cen_obj = objects[segmap[int(bin_msk.shape[0] / 2.), int(bin_msk.shape[1] / 2.)] - 1] fraction_radius = sep.flux_radius(img, cen_obj['x'], cen_obj['y'], 10 * cen_obj['a'], 0.5)[0] ba = np.divide(cen_obj['b'], cen_obj['a']) sep.mask_ellipse(cen_mask, cen_obj['x'], cen_obj['y'], fraction_radius, fraction_radius * ba, cen_obj['theta'], r=r) from astropy.convolution import convolve, Gaussian2DKernel HSC_mask = (TDmask[:, :, 5]).astype(bool) * ( ~cen_mask) + TDmask[:, :, 9].astype(bool) # Convolve the image with a Gaussian kernel with the width of 1.5 pixel cvl = convolve(HSC_mask.astype('float'), Gaussian2DKernel(radius)) HSC_detect_mask = cvl >= threshold return HSC_detect_mask
def get_gauss_beam(fwhm, pixscale, band, nfwhm=5.0, oversamp=1): retext = round(fwhm * nfwhm / pixscale) if retext % 2 == 0: retext += 1 bmsigma = fwhm / math.sqrt(8 * math.log(2)) beam = Gaussian2DKernel(bmsigma / pixscale, x_size=retext, y_size=retext, mode='oversample', factor=oversamp) beam *= 1.0 / beam.array.max() return beam
def maybe_interpolate_dead_pixels(self): if self.interpolate_dead_pixels: print('Interpolating dead pixels...') kernel = Gaussian2DKernel(1) not_valid_and_inside_brightfield = np.logical_and( np.logical_not(self.bvm), self.bmb) self.data[not_valid_and_inside_brightfield] = np.NaN # p = Pool(multiprocessing.cpu_count()) # p.map(replace_nans, data) for i, da in enumerate(self.data): self.data[i] = interpolate_replace_nans(da.copy(), kernel) self.data = np.nan_to_num(self.data, copy=False)
def get_gauss_beam(fwhm, pixscale, nfwhm=5.0, oversamp=1): """ Generate Gaussian kernel Parameters ---------- fwhm: float FWHM of the Gaussian beam. pixscale: float Pixel scale, in same units as FWHM. nfwhm: float Number of fwhm (approximately) of each dimension of the output beam. oversamp: int Odd integer giving the oversampling to use when constructing the beam. The beam is generated in pixscale / oversamp size pixels, then rebinned to pixscale. Notes ----- The beam is normalized by having a value of 1 in the center. If oversampling is used, the returned array will be the sum over the neighborhood of this maximum, so will not be one. """ if fwhm <= 0: raise ValueError("Invalid (negative) FWHM") if pixscale <= 0: raise ValueError("Invalid (negative) pixel scale") if nfwhm <= 0.0: raise ValueError("Invalid (non-positive) nfwhm") if fwhm / pixscale < 2.5: raise ValueError("Insufficiently well sampled beam") if oversamp < 1: raise ValueError("Invalid (<1) oversampling") retext = round(fwhm * nfwhm / pixscale) if retext % 2 == 0: retext += 1 bmsigma = fwhm / math.sqrt(8 * math.log(2)) beam = Gaussian2DKernel(bmsigma / pixscale, x_size=retext, y_size=retext, mode='oversample', factor=oversample) beam *= 1.0 / beam.array.max() return beam
def _compute_asteroid_outlier_mask(self, correction): '''Finds a mask in a corrected cube, where there are extremely bright asteroids.''' b = np.copy(self.flux - correction) # Whiten by pixel time series std dev s = np.atleast_3d(np.nanstd(b, axis=(0))).transpose([2, 0, 1]) # Sum in one dimension, remove median and find 3 sigma outliers ysum = np.nanmax(gaussian_filter(np.nan_to_num(b/s), (1.5, 1.5, 0)), axis=1) ymed = np.atleast_2d(np.nanmedian(ysum, axis=1)).T * np.ones(ysum.shape) yans = ysum - ymed > 3 # Sum in other dimension, remove median and find 3 sigma outliers xsum = np.nanmax(gaussian_filter(np.nan_to_num(b/s), (1.5, 0, 1.5)), axis=2) xmed = np.atleast_2d(np.nanmedian(xsum, axis=1)).T * np.ones(xsum.shape) xans = xsum - xmed > 3 # Convolve with 45 degree gaussians, to make sure if we miss a time stamp we have a shot of getting it. c1 = convolve(yans.astype(float), Gaussian2DKernel(1, 0.2, theta=-45)) c2 = convolve(yans.astype(float), Gaussian2DKernel(1, 0.2, theta=45)) yans = np.any([(c1 > 0.01), (c2 > 0.01)], axis=0) c1 = convolve(xans.astype(float), Gaussian2DKernel(1, 0.2, theta=-45)) c2 = convolve(xans.astype(float), Gaussian2DKernel(1, 0.2, theta=45)) xans = np.any([(c1 > 0.01), (c2 > 0.01)], axis=0) # Find where these two dimensions cross a = (np.atleast_3d(xans) | np.atleast_3d(yans).transpose([0, 2, 1])) # Weak Gaussian blur helps find real sources c = gaussian_filter(np.nan_to_num(b/s), (0, 0.5, 0.5)) # Where there are sources in the image threshold = (c > 5*np.atleast_3d(np.nanstd(c, axis=(1,2))).transpose([1, 0, 2])) # Take any sources that are in the crosshairs and have high SNR. aper = threshold & a return ~aper
def create_seg_map(self): ''' Creates segmentation map, from original FLT file, that is used in background subtraction and to fix cosmic rays. Parameters ---------- self : object DashData object created from an individual IMA file. Output ------ Segmentation Image : fits file Segmentation map Source List : .dat file List of sources and their properties ''' flt = fits.open(self.flt_file_name) data = flt[1].data threshold = detect_threshold(data, nsigma=3.) sigma = 3.0 * gaussian_fwhm_to_sigma # FWHM = 3. kernel = Gaussian2DKernel(sigma, x_size=3, y_size=3) kernel.normalize() segm = detect_sources(data, threshold, npixels=10, filter_kernel=kernel) hdu = fits.PrimaryHDU(segm.data) if not os.path.exists('segmentation_maps'): os.mkdir('segmentation_maps') hdu.writeto(('segmentation_maps/{}_seg.fits').format(self.root), overwrite=True) # Create source list cat = source_properties(data, segm) tbl = cat.to_table() tbl['xcentroid'].info.format = '.2f' tbl['ycentroid'].info.format = '.2f' tbl['cxx'].info.format = '.2f' tbl['cxy'].info.format = '.2f' tbl['cyy'].info.format = '.2f' ascii.write(tbl, 'segmentation_maps/{}_source_list.dat'.format(self.root))
def vue_spatial_convolution(self, *args): """ Use astropy convolution machinery to smooth the spatial dimensions of the data cube. """ size = float(self.stddev) cube = self._selected_data.get_object(cls=SpectralCube) # Extend the 2D kernel to have a length 1 spectral dimension, so that # we can do "3d" convolution to the whole cube kernel = np.expand_dims(Gaussian2DKernel(size), 0) # TODO: in vuetify >2.3, timeout should be set to -1 to keep open # indefinitely snackbar_message = SnackbarMessage( "Smoothing spatial slices of cube...", loading=True, timeout=0, sender=self) self.hub.broadcast(snackbar_message) convolved_data = convolve(cube.hdu.data, kernel) # Create a new cube with the old metadata. Note that astropy # convolution generates values for masked (NaN) data, but we keep the # original mask here. newcube = SpectralCube(data=convolved_data, wcs=cube.wcs, mask=cube.mask, meta=cube.meta, fill_value=cube.fill_value) label = f"Smoothed {self._selected_data.label} spatial stddev {size}" if label in self.data_collection: snackbar_message = SnackbarMessage( "Data with selected stddev already exists, canceling operation.", color="error", sender=self) self.hub.broadcast(snackbar_message) return self.data_collection[label] = newcube snackbar_message = SnackbarMessage( f"Data set '{self._selected_data.label}' smoothed successfully.", color="success", sender=self) self.hub.broadcast(snackbar_message)
def get_gaussian_psf_template(pixel_fwhm=3., nbin=5, normalization='max'): nc = 25 psfnew = Gaussian2DKernel((pixel_fwhm / 2.355) * nbin, x_size=125, y_size=125).array.astype(np.float32) if normalization == 'max': print('Normalizing PSF by kernel maximum') psfnew /= np.max(psfnew) psfnew /= 4 * np.pi * (pixel_fwhm / 2.355)**2 else: print('Normalizing PSF by kernel sum') psfnew *= nc cf = psf_poly_fit(psfnew, nbin=nbin) return psfnew, cf, nc, nbin
def test_image(): # Test dataset parameters x_size_kernel, y_size_kernel = (11, 11) x_size_image, y_size_image = (31, 31) total_excess = 100 total_background = 1000 ones = np.ones((x_size_image, y_size_image)) # Create test dataset kernel = Gaussian2DKernel(3, x_size=x_size_kernel, y_size=y_size_kernel).array excess = total_excess * Gaussian2DKernel( 3, x_size=x_size_image, y_size=y_size_image).array background = total_background * ones / ones.sum() counts = excess + background images = dict(counts=counts, background=background) probability = matched_filter.probability_image(images, kernel) # TODO: try to get a verified result assert_allclose(probability.max(), 0.48409238192500076) significance = matched_filter.significance_image(images, kernel) # TODO: try to get a verified result assert_allclose(significance.max(), 7.2493488182450569)
def detect_sources(pattern):# extracts the light sources from the image (basing on sigma, FWHM, thrsholds...) rot = rotate_img(pattern) threshold = detect_threshold(rot, nsigma=2.) sigma = 3.0 * gaussian_fwhm_to_sigma # FWHM = 3. kernel = Gaussian2DKernel(sigma, x_size=3, y_size=3) kernel.normalize() mean, median, std = sigma_clipped_stats(rot, sigma=3) daofind = DAOStarFinder(fwhm=3.0, threshold=5.*std) sources = daofind(rot - median) for col in sources.colnames: sources[col].info.format = '%.8g' # for consistent table output # Pixel coordinates of the sources x1 = np.array(sources['xcentroid']) y1 = np.array(sources['ycentroid']) return(x1,y1)
def test_to_sunpy_map_invalid_pixel_size(self): m = n = 32 u = generate_uv(m) v = generate_uv(m) u, v = np.meshgrid(u, v) uv = np.array([u, v]).reshape(2, m * n) / unit.arcsec header = {'crval1': 0, 'crval2': 0, 'cdelt1': 1, 'cdelt2': 1} data = Gaussian2DKernel(stddev=2, x_size=n, y_size=m).array mp = Map((data, header)) vis = Visibility.from_map(mp, uv) with pytest.raises(ValueError): vis.to_map((m, n), pixel_size=[1, 2, 3] * unit.arcsec)
def test_compute_ts_map_downsampled(input_maps): """Minimal test of compute_ts_image""" kernel = Gaussian2DKernel(2.5) ts_estimator = TSMapEstimator(method='root brentq', n_jobs=4, error_method='conf', ul_method='conf') result = ts_estimator.run(input_maps, kernel=kernel, downsampling_factor=2) assert_allclose(result['ts'].data[99, 99], 1675.28, rtol=1e-2) assert_allclose(result['niter'].data[99, 99], 7) assert_allclose(result['flux'].data[99, 99], 1.02e-09, rtol=1e-2) assert_allclose(result['flux_err'].data[99, 99], 3.84e-11, rtol=1e-2) assert_allclose(result['flux_ul'].data[99, 99], 1.10e-09, rtol=1e-2)
def test_filtering(self): from astropy.convolution import Gaussian2DKernel FWHM2SIGMA = 1.0 / (2.0 * np.sqrt(2.0 * np.log(2.0))) filter_kernel = Gaussian2DKernel(2.*FWHM2SIGMA, x_size=3, y_size=3) error = np.sqrt(IMAGE) props1 = source_properties(IMAGE, SEGM, error=error) props2 = source_properties(IMAGE, SEGM, error=error, filter_kernel=filter_kernel.array) p1, p2 = props1[0], props2[0] keys = ['source_sum', 'source_sum_err'] for key in keys: assert p1[key] == p2[key] keys = ['semimajor_axis_sigma', 'semiminor_axis_sigma'] for key in keys: assert p1[key] != p2[key]
def get_kernel(res_t, res_c, pix_c): """Get smoothing kernel""" fwhm_factor = np.sqrt(8 * np.log(2)) gaussian_width = ((res_t**2 - res_c**2)**0.5 / pix_c / fwhm_factor) print("[INFO] Current pixel size of %0.1f arcsec" % (pix_c)) print("[INFO] Current resolution of %0.1f arcsec" % (res_c)) print("[INFO] Target resolution of %0.1f arcsec" % (res_t)) print("[INFO] Smoothing with gaussian of %0.1f arcsec" % (gaussian_width * pix_c * fwhm_factor)) kernel = Gaussian2DKernel(gaussian_width) return kernel
def convolveImages(HSTImageObject,radioImageObject): FWHMconst = 2.355 fwhmHSTArcSec = 0.18 sigmaHSTPixInALMA = (fwhmHSTArcSec/FWHMconst)/(radioImageObject.pixScaleArcSec) sigmaHSTPixInHST = (fwhmHSTArcSec/FWHMconst)/(HSTImageObject.pixScaleArcSec) sigmaMajALMAPixInHST = (radioImageObject.bmajArcSec/FWHMconst)/HSTImageObject.pixScaleArcSec sigmaMinALMAPixInHST = (radioImageObject.bminArcSec/FWHMconst)/HSTImageObject.pixScaleArcSec thetaALMADegree = radioImageObject.bpa sigmaMajALMAPixInALMA = (radioImageObject.bmajArcSec/FWHMconst)/radioImageObject.pixScaleArcSec sigmaMinALMAPixInALMA = (radioImageObject.bmajArcSec/FWHMconst)/radioImageObject.pixScaleArcSec beamALMAInHST = Gaussian2DKernel(x_stddev = sigmaMajALMAPixInHST, y_stddev=sigmaMinALMAPixInHST, theta = ((thetaALMADegree +90) * np.pi)/180,x_size = HSTImageObject.imageSize[0],y_size = HSTImageObject.imageSize[1]) beamALMAInALMA= Gaussian2DKernel(x_stddev = sigmaMajALMAPixInALMA, y_stddev=sigmaMinALMAPixInALMA, theta = ((thetaALMADegree +90) * np.pi)/180,x_size = radioImageObject.imageSize[0],y_size = radioImageObject.imageSize[1]) psfHSTInALMA = Gaussian2DKernel(x_stddev = sigmaHSTPixInALMA, y_stddev=sigmaHSTPixInALMA, theta = 0, x_size = radioImageObject.imageSize[0], y_size = radioImageObject.imageSize[1]) psfHSTInHST = Gaussian2DKernel(x_stddev = sigmaHSTPixInHST, y_stddev=sigmaHSTPixInHST, theta = 0, x_size = HSTImageObject.imageSize[0], y_size = HSTImageObject.imageSize[1] ) convolvedUV = convolve_fft(HSTImageObject.data,beamALMAInHST,allow_huge=True) convolvedALMA = convolve_fft(radioImageObject.data,psfHSTInALMA,allow_huge=True) return convolvedUV, convolvedALMA
def gaussian_deriv_kernel(axis=0, stddev=3.0, oned=False): if oned: gauss = Gaussian1DKernel(stddev) else: gauss = Gaussian2DKernel(stddev) x = np.linspace(0, gauss.shape[axis] - 1, gauss.shape[axis]) dkernel = deriv_central(gauss.array, x, axis=axis) if not oned: if axis == 0: dkernel = dkernel[:, 1:-1] elif axis == 1: dkernel = dkernel[1:-1, :] return dkernel / abs(dkernel).sum() else: return dkernel
def restore_cube(model: Image, psf: Image, residual=None, **kwargs) -> Image: """ Restore the model image to the residuals :params psf: Input PSF :return: restored image """ assert isinstance(model, Image), "Type is %s" % (type(model)) assert isinstance(psf, Image), "Type is %s" % (type(psf)) assert residual is None or isinstance( residual, Image), "Type is %s" % (type(residual)) restored = copy_image(model) npixel = psf.data.shape[3] sl = slice(npixel // 2 - 7, npixel // 2 + 8) size = get_parameter(kwargs, "psfwidth", None) if size is None: # isotropic at the moment! try: fit = fit_2dgaussian(psf.data[0, 0, sl, sl]) if fit.x_stddev <= 0.0 or fit.y_stddev <= 0.0: log.debug( 'restore_cube: error in fitting to psf, using 1 pixel stddev' ) size = 1.0 else: size = max(fit.x_stddev, fit.y_stddev) log.debug('restore_cube: psfwidth = %s' % (size)) except: log.debug( 'restore_cube: warning in fit to psf, using 1 pixel stddev') size = 1.0 else: log.debug('restore_cube: Using specified psfwidth = %s' % (size)) # By convention, we normalise the peak not the integral so this is the volume of the Gaussian norm = 2.0 * numpy.pi * size**2 gk = Gaussian2DKernel(size) for chan in range(model.shape[0]): for pol in range(model.shape[1]): restored.data[chan, pol, :, :] = norm * convolve( model.data[chan, pol, :, :], gk, normalize_kernel=False) if residual is not None: restored.data += residual.data return restored
def convolved_grid(N1d: int = 16, border: int = 64, size: int = 1024, kernel: Union[Kernel2D, None] = Gaussian2DKernel(x_stddev=1, x_size=201, y_size=201), perturbation: float = 0., seed: int = 1000) -> Tuple[np.ndarray, Table]: """ Place point sources on a regular image grid and convolve with a kernel to simulate PSF. No noise, distortion etc. :param N1d: Grid of N1d x N1d Stars will be generated :param border: how many pixels on the edge to leave empty :param kernel: What to convolve the point sources with :param perturbation: random uniform offset to star positions :param seed: RNG initializer :return: image, input catalogue """ # Kernel should always be an odd image or else we introduce some shift in the image np.random.seed(seed) data = np.zeros((size, size)) idx_float = np.linspace(0 + border, size - border, N1d) x_float = np.tile(idx_float, reps=(N1d, 1)) y_float = x_float.T.copy() # just a view of x_float # these two modify same array... x_float += np.random.uniform(0, perturbation, x_float.shape) y_float += np.random.uniform(0, perturbation, y_float.shape) x, x_frac = np.divmod(x_float, 1) y, y_frac = np.divmod(y_float, 1) x, y = x.astype(int), y.astype(int) # Y U so ugly sometimes PEP8? data[y, x] = (1 - x_frac) * (1 - y_frac) # noinspection PyRedundantParentheses data[y + 1, x] = (1 - x_frac) * (y_frac) # noinspection PyRedundantParentheses data[y, x + 1] = (x_frac) * (1 - y_frac) data[y + 1, x + 1] = y_frac * x_frac if kernel is not None: # noinspection PyTypeChecker data = convolve_fft(data, kernel) # TODO the no-zeros seem like an awful hack data = data / np.max(data) + 0.001 # normalize and add tiny offset to have no zeros in data fluxes = np.ones(x.size) table = Table((x_float.ravel(), y_float.ravel(), fluxes, flux_to_magnitude(fluxes)), names=COLUMN_NAMES) return data, table
def replacenans_2d_interpolate(data): """Interpolate over nans within array using astropy interpolate_replace_nans function Input: data = np.array of data values Output: data_out = np.array of data value with no nan values""" # We smooth with a Gaussian kernel with x_stddev=1 (and y_stddev=1) # It is a 9x9 array kernel = Gaussian2DKernel(x_stddev=1) # create a "fixed" image with NaNs replaced by interpolated values data_out = interpolate_replace_nans(data, kernel) return data_out
def make_significance_image(): """Make significance = sqrt(TS) image using a Gaussian kernel. """ gauss_kernel_sigma = 5 # pix header = fits.getheader(REF_IMAGE) counts = fits.getdata(COUNTS_IMAGE) background = fits.getdata(BACKGROUND_IMAGE) exposure = 1e11 * np.ones_like(counts, dtype='float32') kernel = Gaussian2DKernel(gauss_kernel_sigma) print('Computing TS image ...') result = compute_ts_map(counts, background, exposure, kernel) print('TS map computation took: {}'.format(result.runtime)) print('Writing {}'.format(TS_IMAGES)) result.write(TS_IMAGES, header=header, overwrite=True)
def _daofind_kernel(self): """ The DAOFind kernel, a 2D circular Gaussian normalized to have zero sum. """ size = self._kernel_size kernel = Gaussian2DKernel(self.kernel_sigma, x_size=size, y_size=size).array kernel /= np.max(kernel) kernel *= self._kernel_mask # normalize the kernel to zero sum npixels = self._kernel_mask.sum() denom = np.sum(kernel**2) - (np.sum(kernel)**2 / npixels) return (((kernel - (kernel.sum() / npixels)) / denom) * self._kernel_mask)
def test_dftmap_decentered(self, m, n, pos): data = Gaussian2DKernel(stddev=2, x_size=m, y_size=n).array data2 = shift(data, (pos[1], pos[0])) ut = (np.arange(m) - m / 2 + 0.5) * (1 / m) vt = -1.0 * (np.arange(n) - n / 2 + 0.5) * (1 / n) u, v = np.meshgrid(ut, vt) uv = np.array([u, v]).reshape(2, m * n) dft_data = Visibility.dft_map(data2, uv, center=pos) idft_data = Visibility.idft_map(dft_data, np.zeros((m, n)), uv) assert np.allclose(data, idft_data) dft_data2 = Visibility.dft_map(data, uv) idft_data2 = Visibility.idft_map(dft_data2, np.zeros((m, n)), uv, pos) assert np.allclose(idft_data2, data2)