def __init__(self, cube, beam=None, mask=None, method="MAD", compute=True): # Initialize cube object self.cube = SpectralCube.read(cube) if mask is not None: _check_mask(mask) self.mask = mask if beam is not None: _check_beam(mask) # Initialize noise object self.noise = Noise(self.cube, beam=beam, method=method)
def moment_masking(cube, kernel_size, clip=5, dilations=1): ''' ''' if not signal_id_flag: raise ImportError("signal-id is not installed." " This function is not available.") smooth_data = convolve(cube.filled_data[:], gauss_kern(kernel_size)) fake_mask = LazyMask(np.isfinite, cube=cube) smooth_cube = SpectralCube(data=smooth_data, wcs=cube.wcs, mask=fake_mask) smooth_scale = Noise(smooth_cube).scale mask = (smooth_cube > (clip * smooth_scale)).include() # Now dilate the mask once dilate_struct = nd.generate_binary_structure(3, 3) mask = nd.binary_dilation(mask, structure=dilate_struct, iterations=dilations) return mask
def _update(self, data=None, wcs=None, beam=None, method="MAD"): ''' Helper function to update classes. ''' # Check if we need a new SpectralCube if data is None and wcs is None: pass else: if data is None: data = self.cube.unmasked_data[:] if wcs is None: wcs = self.cube.wcs # Make new SpectralCube object self.cube = SpectralCube(data=data, wcs=wcs) if beam is not None: _check_beam(beam) self.noise = Noise(self.cube, beam=beam, method=method)
def __init__(self, cube, beam=None, mask=None, method="MAD", compute=True): raise NotImplementedError("SimCube is not yet implemented for " "general use.") # Initialize cube object self.cube = SpectralCube.read(cube) if mask is not None: _check_mask(mask) self.mask = mask if beam is not None: _check_beam(mask) # Initialize noise object self.noise = Noise(self.cube, beam=beam, method=method)
def __init__(self, cube, mask=None, algorithm=None, beam=None): super(ObsCube, self).__init__() self.cube = sc.SpectralCube.read(cube) self.algorithm = algorithm # Make sure mask is an accepted type if mask is not None: _check_mask(mask) self.mask = mask if beam is not None: _check_beam(beam) self.noise = Noise(self.cube, beam=beam)
def __init__(self, cube, noise_type='constant', clip=3, scale=None, moment_method='slice'): super(Mask_and_Moments, self).__init__() if not spectral_cube_flag: raise ImportError("Mask_and_Moments requires the spectral-cube " " to be installed: https://github.com/" "radio-astro-tools/spectral-cube") if isinstance(cube, SpectralCube): self.cube = cube self.save_name = None else: self.cube = SpectralCube.read(cube) # Default save name to the cube name without the suffix. self.save_name = ".".join(cube.split(".")[:-1]) self.noise_type = noise_type self.clip = clip if moment_method not in ['slice', 'cube', 'ray']: raise TypeError("Moment method must be 'slice', 'cube', or 'ray'.") self.moment_how = moment_method if scale is None: if not signal_id_flag: raise ImportError("signal-id is not installed and error" " estimation is not available. You must " "provide the noise scale.") self.scale = Noise(self.cube).scale * self.cube.unit else: if not isinstance(scale, u.Quantity): raise TypeError("scale must be a Quantity with the same units" " as the given cube.") if not scale.unit == self.cube.unit: raise u.UnitsError("scale must have the same units" " as the given cube.") self.scale = scale self.prop_headers = None self.prop_err_headers = None
def __init__(self, cube, mask=None, algorithm=None, beam=None): raise NotImplementedError("ObsCube is not yet implemented for " "general use.") super(ObsCube, self).__init__() self.cube = SpectralCube.read(cube) self.algorithm = algorithm # Make sure mask is an accepted type if mask is not None: _check_mask(mask) self.mask = mask if beam is not None: _check_beam(beam) self.noise = Noise(self.cube, beam=beam)
def find_noise(self, return_obj=False): ''' Returns noise estimate, or the whole Noise object. Parameters ---------- return_obj : bool, optional If True, returns the Noise object. Otherwise returns the estimated noise level. ''' if not signal_id_flag: raise ImportError("signal-id is not installed." " This function is not available.") noise = Noise(self.cube) self.scale = noise.scale if return_obj: return noise return noise.scale
smooth_chans = int(round_up_to_odd(200. / 66.)) # 10 consecutive channels must be above the MAD level to be real emission. num_chans = 7 peak_snr = 4.5 # Cutoff level min_snr = 3 # Where to cut at the line edges edge_thresh = 0.5 # Smooth the cube, then create a noise model spec_kernel = Box1DKernel(smooth_chans) smooth_cube = cube.spectral_smooth(spec_kernel) noise = Noise(smooth_cube) noise.estimate_noise(spectral_flat=True) noise.get_scale_cube() snr = noise.snr.copy() posns = np.where(snr.max(axis=0) >= min_snr) bad_pos = np.where(snr.max(axis=0) < min_snr) mask[:, bad_pos[0], bad_pos[1]] = False # In case single spectra need to be inspected. verbose = False for i, j in ProgressBar(zip(*posns)):
def make_signal_mask(cube, smooth_chans=200. / 66., min_chan=7, peak_snr=5., min_snr=3.5, edge_thresh=1.5, verbose=False): ''' Create a robust signal mask by requiring spatial and spectral connectivity. ''' import astropy.units as u from astropy.convolution import Box1DKernel from signal_id import Noise from scipy import ndimage as nd from astropy.wcs.utils import proj_plane_pixel_scales from astropy.utils.console import ProgressBar import skimage.morphology as mo import numpy as np from radio_beam import Beam from itertools import groupby, chain from operator import itemgetter import matplotlib.pyplot as p pixscale = proj_plane_pixel_scales(cube.wcs)[0] # # Want to smooth the mask edges mask = cube.mask.include().copy() # Set smoothing parameters and # consecutive channels. smooth_chans = int(round_up_to_odd(smooth_chans)) # consecutive channels to be real emission. num_chans = min_chan # Smooth the cube, then create a noise model spec_kernel = Box1DKernel(smooth_chans) smooth_cube = cube.spectral_smooth(spec_kernel) noise = Noise(smooth_cube) noise.estimate_noise(spectral_flat=True) noise.get_scale_cube() snr = noise.snr.copy() snr[np.isnan(snr)] = 0.0 posns = np.where(snr.max(axis=0) >= min_snr) bad_pos = np.where(snr.max(axis=0) < min_snr) mask[:, bad_pos[0], bad_pos[1]] = False for i, j in ProgressBar(zip(*posns)): # Look for all pixels above min_snr good_posns = np.where(snr[:, i, j] > min_snr)[0] # Reject if the total is less than connectivity requirement if good_posns.size < num_chans: mask[:, i, j] = False continue # Find connected pixels sequences = [] for k, g in groupby(enumerate(good_posns), lambda (i, x): i - x): sequences.append(map(itemgetter(1), g)) # Check length and peak. Require a minimum of 3 pixels above the noise # to grow from. sequences = [seq for seq in sequences if len(seq) >= 3 and np.nanmax(snr[:, i, j][seq]) >= peak_snr] # Continue if no good sequences found if len(sequences) == 0: mask[:, i, j] = False continue # Now take each valid sequence and expand the edges until the smoothed # spectrum approaches zero. edges = [[seq[0], seq[-1]] for seq in sequences] for n, edge in enumerate(edges): # Lower side if n == 0: start_posn = edge[0] stop_posn = 0 else: start_posn = edge[0] - edges[n - 1][0] stop_posn = edges[n - 1][0] for pt in np.arange(start_posn, stop_posn, -1): # if smoothed[pt] <= mad * edge_thresh: if snr[:, i, j][pt] <= edge_thresh: break sequences[n].insert(0, pt) # Upper side start_posn = edge[1] if n == len(edges) - 1: stop_posn = cube.shape[0] else: stop_posn = edges[n + 1][0] for pt in np.arange(start_posn, stop_posn, 1): # if smoothed[pt] <= mad * edge_thresh: if snr[:, i, j][pt] <= edge_thresh: break sequences[n].insert(0, pt) # Final check for the min peak level and ensure all meet the # spectral connectivity requirement sequences = [seq for seq in sequences if len(seq) >= num_chans and np.nanmax(snr[:, i, j][seq]) >= peak_snr] if len(sequences) == 0: mask[:, i, j] = False continue bad_posns = \ list(set(np.arange(cube.shape[0])) - set(list(chain(*sequences)))) mask[:, i, j][bad_posns] = False if verbose: p.subplot(121) p.plot(cube.spectral_axis.value, noise.snr[:, i, j]) min_val = cube.spectral_axis.value[np.where(mask[:, i, j])[0][-1]] max_val = cube.spectral_axis.value[np.where(mask[:, i, j])[0][0]] p.vlines(min_val, 0, np.nanmax(noise.snr[:, i, j])) p.vlines(max_val, 0, np.nanmax(noise.snr[:, i, j])) p.plot(cube.spectral_axis.value, noise.snr[:, i, j] * mask[:, i, j], 'bD') p.subplot(122) p.plot(cube.spectral_axis.value, cube[:, i, j], label='Cube') p.plot(cube.spectral_axis.value, smooth_cube[:, i, j], label='Smooth Cube') p.axvline(min_val) p.axvline(max_val) p.plot(cube.spectral_axis.value, smooth_cube[:, i, j] * mask[:, i, j], 'bD') p.draw() raw_input("Next spectrum?") p.clf()
sub1.show_colorbar() sub2.show_colorbar() sub1.colorbar.set_axis_label_text("Intensity (Jy/beam)") sub2.hide_yaxis_label() chan_fig.tight_layout() raw_input("Continue?") chan_fig.close() # Now create good masks and derive 0th moments. noise_cube = Noise(cube) new_noise = noise_cube.scale cube = cube.with_mask(cube > new_noise*u.Jy) old_noise_cube = Noise(old_cube) old_noise = old_noise_cube.scale old_cube = old_cube.with_mask(old_cube > old_noise*u.Jy) # Load in the broad clean mask used clean_mask = fits.getdata("../../../Arecibo/M33_newmask.fits") # Need to match the dims
class SimCube(object): ''' A wrapping class to prepare a simulated spectral data cube for comparison with another cube. ''' def __init__(self, cube, beam=None, mask=None, method="MAD", compute=True): # Initialize cube object self.cube = SpectralCube.read(cube) if mask is not None: _check_mask(mask) self.mask = mask if beam is not None: _check_beam(mask) # Initialize noise object self.noise = Noise(self.cube, beam=beam, method=method) def add_noise(self): ''' Use Noise to add synthetic noise to the data. Then update SpectralCube. ''' # Create the noisy cube self.noise.get_noise_cube() noise_data = self.noise.noise_cube +\ self.cube.filled_data[:] # Update SpectralCube object self._update(data=noise_data) return self def clean_cube(self, algorithm=None): raise NotImplementedError("") def apply_mask(self, mask=None): ''' Check if the given mask is acceptable abd apply to SpectralCube. ''' # Update mask if mask is not None: _check_mask(mask) self.mask = mask # Create the mask, auto masking nan values default_mask = np.isfinite(self.cube.filled_data[:]) if self.mask is not None: self.mask = CompositeMask(default_mask, self.mask) else: self.mask = default_mask # Apply mask to spectral cube object self.cube = self.cube.with_mask(mask) return self def _update(self, data=None, wcs=None, beam=None, method="MAD"): ''' Helper function to update classes. ''' # Check if we need a new SpectralCube if data is None and wcs is None: pass else: if data is None: data = self.cube.unmasked_data[:] if wcs is None: wcs = self.cube.wcs # Make new SpectralCube object self.cube = SpectralCube(data=data, wcs=wcs) if beam is not None: _check_beam(beam) self.noise = Noise(self.cube, beam=beam, method=method) def compute_properties(self): ''' Use SpectralCube to compute the moments. Also compute the integrated intensity based on the noise properties from Noise. ''' self._moment0 = self.cube.moment0().value self._moment1 = self.cube.moment1().value self._moment2 = self.cube.moment2().value _get_int_intensity(self) return self @property def moment0(self): return self._moment0 @property def moment1(self): return self._moment1 @property def moment2(self): return self._moment2 @property def intint(self): return self._intint def sim_prep(self, mask=None): ''' Prepares the cube when being compared to another simulation. This entails: * Optionally applying a mask to the data. * Computing the cube's property arrays ''' if not mask is None: self.apply_mask() self.compute_properties() return self def obs_prep(self, mask=None): ''' Prepares the cube when being compared to observational data. This entails: * Optionally applying a mask to the data. * Add synthetic noise based on the cube's properties. * Computing the cube's property arrays ''' if not mask is None: self.apply_mask() self.add_noise() self.compute_properties() return self