예제 #1
0
def flaglos(cube, threshold, dilation):
    rms0 = GetRMS(cube,
                  rmsMode="mad",
                  fluxRange="negative",
                  zoomx=1,
                  zoomy=1,
                  zoomz=1,
                  verbose=0,
                  twoPass=True)

    # COMPACT ...
    #los_rms = np.nanstd(cube, axis=0)
    #los_rms = 1.4826 * np.nanmedian(abs(cube), axis=0)

    # ... OR NOT in order to use the TwoPass option of GetRMS
    los_rms = np.zeros((cube.shape[-2], cube.shape[-1]))
    for xx in range(cube.shape[-1]):
        for yy in range(cube.shape[-2]):
            los_rms[yy, xx] = GetRMS(cube[:, yy:yy + 1, xx:xx + 1],
                                     rmsMode="mad",
                                     fluxRange="all",
                                     twoPass=True)

    # Mask all LOS whose RMS is > threshold*STD above rms0 (optionally extended to neighbouring LOS using binary dilation with a box structuring element)
    los_rms_disp = np.nanstd(los_rms)
    los_rms = (los_rms < rms0 + threshold * los_rms_disp)
    los_rms = binary_dilation(~los_rms,
                              structure=np.ones((dilation, dilation)))
    cube[:, los_rms] = np.nan

    return cube
예제 #2
0
def filter(mask, cube, header, clipMethod, threshold, rmsMode, fluxRange, verbose):
	err.message("Running threshold finder.")
	
	# Sanity checks of user input
	err.ensure(
		clipMethod in {"absolute", "relative"},
		"Threshold finder failed. Illegal clip method: '" + str(clipMethod) + "'.")
	err.ensure(
		rmsMode in {"std", "mad", "gauss", "negative"},
		"Threshold finder failed. Illegal RMS mode: '" + str(rmsMode) + "'.")
	err.ensure(
		fluxRange in {"positive", "negative", "all"},
		"Threshold finder failed. Illegal flux range: '" + str(fluxRange) + "'.")
	
	# Scale threshold by RMS if requested
	if clipMethod == "relative":
		threshold *= GetRMS(cube, rmsMode=rmsMode, fluxRange=fluxRange, zoomx=1, zoomy=1, zoomz=1, verbose=verbose)
	
	# Print some information and check sign of threshold
	err.message("  Using threshold of " + str(threshold) + ".")
	err.ensure(threshold >= 0.0, "Threshold finder failed. Threshold value is negative.")
	
	# Run the threshold finder, setting bit 1 of the mask for |cube| >= |threshold|:
	np.bitwise_or(mask, np.greater_equal(np.absolute(cube), threshold), out=mask)
	
	return
예제 #3
0
def sigma_scale(cube,
                scaleX=False,
                scaleY=False,
                scaleZ=True,
                edgeX=0,
                edgeY=0,
                edgeZ=0,
                statistic="mad",
                fluxRange="all",
                method="global",
                windowSpatial=20,
                windowSpectral=20,
                gridSpatial=0,
                gridSpectral=0,
                interpolation="none"):
    # Print some informational messages
    err.message("Generating noise-scaled data cube:")
    err.message("  Selecting " + str(method) + " noise measurement method.")

    if statistic == "mad":
        err.message("  Applying median absolute deviation to " +
                    str(fluxRange) + " pixels.")
    if statistic == "std":
        err.message("  Applying standard deviation to " + str(fluxRange) +
                    " pixels.")
    if statistic == "gauss":
        err.message("  Applying Gaussian fit to " + str(fluxRange) +
                    " pixels.")
    if statistic == "negative":
        err.message("  Applying Gaussian fit to negative pixels.")

    # Check the dimensions of the cube (could be obtained from header information)
    dimensions = np.shape(cube)

    # LOCAL noise measurement within running window (slower and less memory-friendly)
    if method == "local":
        # Make window sizes integers >= 1
        windowSpatial = max(int(windowSpatial), 1)
        windowSpectral = max(int(windowSpectral), 1)

        # Ensure that window sizes are odd
        windowSpatial += (1 - windowSpatial % 2)
        windowSpectral += (1 - windowSpectral % 2)

        # Set grid sizes to half the window sizes if undefined
        if not gridSpatial: gridSpatial = windowSpatial // 2
        if not gridSpectral: gridSpectral = windowSpectral // 2

        # Make grid sizes integers >= 1
        gridSpatial = max(int(gridSpatial), 1)
        gridSpectral = max(int(gridSpectral), 1)

        # Ensure that grid sizes are odd
        gridSpatial += (1 - gridSpatial % 2)
        gridSpectral += (1 - gridSpectral % 2)

        # Print grid and window sizes adopted
        err.message("  Using grid size of [" + str(gridSpatial) + ", " +
                    str(gridSpectral) + "]")
        err.message("  and window size of [" + str(windowSpatial) + ", " +
                    str(windowSpectral) + "].")

        # Generate grid points to be used
        gridPointsZ = np.arange(
            (dimensions[0] - gridSpectral *
             (int(math.ceil(float(dimensions[0]) / float(gridSpectral))) - 1))
            // 2, dimensions[0], gridSpectral)
        gridPointsY = np.arange(
            (dimensions[1] - gridSpatial *
             (int(math.ceil(float(dimensions[1]) / float(gridSpatial))) - 1))
            // 2, dimensions[1], gridSpatial)
        gridPointsX = np.arange(
            (dimensions[2] - gridSpatial *
             (int(math.ceil(float(dimensions[2]) / float(gridSpatial))) - 1))
            // 2, dimensions[2], gridSpatial)

        # Divide grid and window sizes by 2 to get radii
        radiusGridSpatial = gridSpatial // 2
        radiusGridSpectral = gridSpectral // 2
        radiusWindowSpatial = windowSpatial // 2
        radiusWindowSpectral = windowSpectral // 2

        # Create empty cube (filled with NaN) to hold noise values
        rms_cube = np.full(cube.shape, np.nan, dtype=cube.dtype)

        # Determine RMS across window centred on grid cell
        for z in gridPointsZ:
            for y in gridPointsY:
                for x in gridPointsX:
                    grid = (max(0, z - radiusGridSpectral),
                            min(dimensions[0], z + radiusGridSpectral + 1),
                            max(0, y - radiusGridSpatial),
                            min(dimensions[1], y + radiusGridSpatial + 1),
                            max(0, x - radiusGridSpatial),
                            min(dimensions[2], x + radiusGridSpatial + 1))

                    window = (max(0, z - radiusWindowSpectral),
                              min(dimensions[0], z + radiusWindowSpectral + 1),
                              max(0, y - radiusWindowSpatial),
                              min(dimensions[1], y + radiusWindowSpatial + 1),
                              max(0, x - radiusWindowSpatial),
                              min(dimensions[2], x + radiusWindowSpatial + 1))

                    if not np.all(
                            np.isnan(
                                cube[window[0]:window[1], window[2]:window[3],
                                     window[4]:window[5]])):
                        if interpolation == "linear" or interpolation == "cubic":
                            # Write value into grid point for later interpolation
                            rms_cube[z, y,
                                     x] = GetRMS(cube[window[0]:window[1],
                                                      window[2]:window[3],
                                                      window[4]:window[5]],
                                                 rmsMode=statistic,
                                                 fluxRange=fluxRange,
                                                 zoomx=1,
                                                 zoomy=1,
                                                 zoomz=1,
                                                 verbose=0)
                        else:
                            # Fill entire grid cell
                            rms_cube[grid[0]:grid[1], grid[2]:grid[3],
                                     grid[4]:grid[5]] = GetRMS(
                                         cube[window[0]:window[1],
                                              window[2]:window[3],
                                              window[4]:window[5]],
                                         rmsMode=statistic,
                                         fluxRange=fluxRange,
                                         zoomx=1,
                                         zoomy=1,
                                         zoomz=1,
                                         verbose=0)
                    del grid, window

        # Carry out interpolation if requested, taking NaNs into account
        if interpolation == "linear" or interpolation == "cubic":
            err.message("  Interpolating in between grid points (" +
                        str(interpolation) + ").")

            # First across each spatial plane
            if gridSpatial > 1:
                for z in gridPointsZ:
                    for y in gridPointsY:
                        data_values = rms_cube[z, y, gridPointsX]
                        not_nan = np.logical_not(np.isnan(data_values))
                        if any(not_nan):
                            interp_coords = np.arange(0, dimensions[2])
                            if interpolation == "cubic":
                                spline = InterpolatedUnivariateSpline(
                                    gridPointsX[not_nan], data_values[not_nan])
                                rms_cube[z, y, 0:dimensions[2]] = spline(
                                    interp_coords)
                                del spline
                            else:
                                interp_values = np.interp(
                                    interp_coords, gridPointsX[not_nan],
                                    data_values[not_nan])
                                rms_cube[z, y, 0:dimensions[2]] = interp_values
                                del interp_values
                            del interp_coords
                        del data_values, not_nan
                    for x in range(dimensions[2]):
                        data_values = rms_cube[z, gridPointsY, x]
                        not_nan = np.logical_not(np.isnan(data_values))
                        if any(not_nan):
                            interp_coords = np.arange(0, dimensions[1])
                            if interpolation == "cubic":
                                spline = InterpolatedUnivariateSpline(
                                    gridPointsY[not_nan], data_values[not_nan])
                                rms_cube[z, 0:dimensions[1],
                                         x] = spline(interp_coords)
                                del spline
                            else:
                                interp_values = np.interp(
                                    interp_coords, gridPointsY[not_nan],
                                    data_values[not_nan])
                                rms_cube[z, 0:dimensions[1], x] = interp_values
                                del interp_values
                            del interp_coords
                        del data_values, not_nan
                    # Alternative option: 2-D spatial interpolation using SciPy's interp2d
                    #from scipy.interpolate import interp2d
                    #xx, yy = np.meshgrid(gridPointsX, gridPointsY)
                    #data_values = rms_cube[z, yy, xx]
                    #f = interp2d(gridPointsX, gridPointsY, data_values, kind="cubic")
                    #interp_coords_x = np.arange(0, dimensions[2])
                    #interp_coords_y = np.arange(0, dimensions[1])
                    #rms_cube[z, :, :] = f(interp_coords_x, interp_coords_y)

            # Then along the spectral axis
            if gridSpectral > 1:
                for y in range(dimensions[1]):
                    for x in range(dimensions[2]):
                        data_values = rms_cube[gridPointsZ, y, x]
                        not_nan = np.logical_not(np.isnan(data_values))
                        if any(not_nan):
                            interp_coords = np.arange(0, dimensions[0])
                            if interpolation == "cubic":
                                spline = InterpolatedUnivariateSpline(
                                    gridPointsZ[not_nan], data_values[not_nan])
                                rms_cube[0:dimensions[0], y,
                                         x] = spline(interp_coords)
                                del spline
                            else:
                                interp_values = np.interp(
                                    interp_coords, gridPointsZ[not_nan],
                                    data_values[not_nan])
                                rms_cube[0:dimensions[0], y, x] = interp_values
                                del interp_values
                            del interp_coords
                        del data_values, not_nan

        # Replace any invalid RMS values with NaN
        with np.errstate(invalid="ignore"):
            rms_cube[rms_cube <= 0] = np.nan

        # Divide data cube by RMS cube
        cube /= rms_cube

        # Delete the RMS cube again to release its memory
        #del rms_cube

    # GLOBAL noise measurement on entire 2D plane (faster and more memory-friendly)
    else:
        # Define the range over which statistics are calculated
        z1 = int(edgeZ)
        z2 = int(dimensions[0] - edgeZ)
        y1 = int(edgeY)
        y2 = int(dimensions[1] - edgeY)
        x1 = int(edgeX)
        x2 = int(dimensions[2] - edgeX)

        # Make sure edges don't exceed cube size
        err.ensure(z1 < z2 and y1 < y2 and x1 < x2,
                   "Edge size exceeds cube size for at least one axis.")

        # Create empty cube (filled with 1) to hold noise values
        rms_cube = np.ones(cube.shape, dtype=cube.dtype)

        # Measure noise across 2D planes and scale cube accordingly
        if scaleZ:
            for i in range(dimensions[0]):
                if not np.all(np.isnan(cube[i, y1:y2, x1:x2])):
                    rms = GetRMS(cube[i, y1:y2, x1:x2],
                                 rmsMode=statistic,
                                 fluxRange=fluxRange,
                                 zoomx=1,
                                 zoomy=1,
                                 zoomz=1,
                                 verbose=0)
                    if rms > 0:
                        rms_cube[i, :, :] *= rms
                        cube[i, :, :] /= rms

        if scaleY:
            for i in range(dimensions[1]):
                if not np.all(np.isnan(cube[z1:z2, i, x1:x2])):
                    rms = GetRMS(cube[z1:z2, i, x1:x2],
                                 rmsMode=statistic,
                                 fluxRange=fluxRange,
                                 zoomx=1,
                                 zoomy=1,
                                 zoomz=1,
                                 verbose=0)
                    if rms > 0:
                        rms_cube[:, i, :] *= rms
                        cube[:, i, :] /= rms

        if scaleX:
            for i in range(dimensions[2]):
                if not np.all(np.isnan(cube[z1:z2, y1:y2, i])):
                    rms = GetRMS(cube[z1:z2, y1:y2, i],
                                 rmsMode=statistic,
                                 fluxRange=fluxRange,
                                 zoomx=1,
                                 zoomy=1,
                                 zoomz=1,
                                 verbose=0)
                    if rms > 0:
                        rms_cube[:, :, i] *= rms
                        cube[:, :, i] /= rms

    err.message("Noise-scaled data cube generated.\n")

    return cube, rms_cube
예제 #4
0
def SCfinder_mem(cube,
                 mask,
                 header,
                 t0,
                 kernels=[
                     [0, 0, 0, "b"],
                 ],
                 threshold=3.5,
                 sizeFilter=0,
                 maskScaleXY=2.0,
                 maskScaleZ=2.0,
                 kernelUnit="pixel",
                 edgeMode="constant",
                 rmsMode="negative",
                 fluxRange="all",
                 verbose=0,
                 perSCkernel=False,
                 scaleX=False,
                 scaleY=False,
                 scaleZ=True,
                 edgeX=0,
                 edgeY=0,
                 edgeZ=0,
                 method="1d2d",
                 windowSpatial=20,
                 windowSpectral=20,
                 gridSpatial=0,
                 gridSpectral=0,
                 interpolation="none"):
    # Define a few constants
    FWHM_CONST = 2.0 * math.sqrt(2.0 * math.log(
        2.0))  # Conversion between sigma and FWHM of Gaussian function
    MAX_PIX_CONST = 1.0e+6  # Maximum number of pixels for noise calculation; sampling is set accordingly

    # Check for NaN in cube
    found_nan = np.isnan(cube).any()

    # Set sampling sampleRms for rms measurement
    sampleRms = max(
        1,
        int((float(cube.size) / MAX_PIX_CONST)**(1.0 /
                                                 min(3, len(cube.shape)))))

    # Measure noise in original cube with sampling "sampleRms"
    rms = GetRMS(cube,
                 rmsMode=rmsMode,
                 fluxRange=fluxRange,
                 zoomx=1,
                 zoomy=1,
                 zoomz=1,
                 verbose=verbose,
                 sample=sampleRms)

    # Loop over all kernels
    for kernel in kernels:
        [kx, ky, kz, kt] = kernel
        if verbose:
            err.linebreak()
            err.print_progress_time(t0)
            err.message("    Filter {0:} {1:} {2:} {3:} ...".format(
                kx, ky, kz, kt))
        if kernelUnit == "world" or kernelUnit == "w":
            if verbose: err.message("    Converting filter size to pixels ...")
            kx = abs(float(kx) / header["CDELT1"])
            ky = abs(float(ky) / header["CDELT2"])
            kz = abs(float(kz) / header["CDELT3"])
        if kt == "b":
            if kz != int(math.ceil(kz)) and verbose:
                err.warning(
                    "Rounding width of boxcar z kernel to next integer.")
            kz = int(math.ceil(kz))

        # Create a copy of the original cube
        cube_smooth = np.copy(cube)

        # Replace all NaNs with zero
        if found_nan:
            cube_smooth[np.isnan(cube)] = 0.0

        cube_smooth[(cube_smooth > 0) & (mask > 0)] = maskScaleXY * rms
        cube_smooth[(cube_smooth < 0) & (mask > 0)] = -maskScaleXY * rms

        # Spatial smoothing
        if kx + ky:
            cube_smooth = ndimage.filters.gaussian_filter(
                cube_smooth, [0, ky / FWHM_CONST, kx / FWHM_CONST],
                mode=edgeMode)

        # Spectral smoothing
        if kz:
            if kt == "b":
                cube_smooth = ndimage.filters.uniform_filter1d(cube_smooth,
                                                               kz,
                                                               axis=0,
                                                               mode=edgeMode)
            elif kt == "g":
                cube_smooth = ndimage.filters.gaussian_filter1d(cube_smooth,
                                                                kz /
                                                                FWHM_CONST,
                                                                axis=0,
                                                                mode=edgeMode)

        # Re-insert the NaNs taken out earlier
        if found_nan:
            cube_smooth[np.isnan(cube)] = np.nan

        # Per-kernel noise normalisation (Time consuming!)
        if perSCkernel:
            cube_smooth, noise_smooth = sigma_scale(
                cube_smooth,
                scaleX=scaleX,
                scaleY=scaleY,
                scaleZ=scaleZ,
                edgeX=edgeX,
                edgeY=edgeY,
                edgeZ=edgeZ,
                statistic=rmsMode,
                fluxRange=fluxRange,
                method=method,
                windowSpatial=windowSpatial,
                windowSpectral=windowSpectral,
                gridSpatial=gridSpatial,
                gridSpectral=gridSpectral,
                interpolation=interpolation)

        # Calculate the RMS of the smoothed (possibly normalised) cube
        rms_smooth = GetRMS(cube_smooth,
                            rmsMode=rmsMode,
                            fluxRange=fluxRange,
                            zoomx=1,
                            zoomy=1,
                            zoomz=1,
                            verbose=verbose,
                            sample=sampleRms)

        # Add pixels above threshold to mask by setting bit 1
        err.message("    Applying +/- {0:} sigma detection threshold".format(
            threshold))
        with np.errstate(invalid="ignore"):
            mask |= (np.absolute(cube_smooth) >= threshold * rms_smooth)
            #mask = np.bitwise_or(mask, np.greater_equal(np.absolute(cube_smooth), threshold * rms_smooth))

        # Delete smoothed cube again
        del cube_smooth
    return
예제 #5
0
def SCfinder_mem(cube, header, t0, kernels=[[0, 0, 0, "b"],], threshold=3.5, sizeFilter=0, maskScaleXY=2.0, maskScaleZ=2.0, kernelUnit="pixel", edgeMode="constant", rmsMode="negative", fluxRange="all", verbose=0):
	# Define a few constants
	FWHM_CONST    = 2.0 * math.sqrt(2.0 * math.log(2.0))   # Conversion between sigma and FWHM of Gaussian function
	MAX_PIX_CONST = 1e+6                                   # Maximum number of pixels for noise calculation; sampling is set accordingly
	
	# Create binary mask array
	msk = np.zeros(cube.shape, np.bool)
	found_nan = np.isnan(cube).sum()
	
	# Set sampling sampleRms for rms measurement
	sampleRms = max(1, int((float(np.array(cube.shape).prod()) / MAX_PIX_CONST)**(1.0 / min(3, len(cube.shape)))))
	
	# Measure noise in original cube with sampling "sampleRms"
	rms = GetRMS(cube, rmsMode=rmsMode, fluxRange=fluxRange, zoomx=1, zoomy=1, zoomz=1, verbose=verbose, sample=sampleRms)
	
	# Loop over all kernels
	for kernel in kernels:
		[kx, ky, kz, kt] = kernel
		if verbose:
			err.linebreak()
			err.print_progress_time(t0)
			err.message("    Filter %s %s %s %s ..." % (kx, ky, kz, kt))
		if kernelUnit == "world" or kernelUnit == "w":
			if verbose: err.message("    Converting filter size to pixels ...")
			kx = abs(float(kx) / header["CDELT1"])
			ky = abs(float(ky) / header["CDELT2"])
			kz = abs(float(kz) / header["CDELT3"])
		if kt == "b":
			if kz != int(math.ceil(kz)) and verbose: err.warning("Rounding width of boxcar z kernel to next integer.")
			kz = int(math.ceil(kz))
		
		# Create a copy of the original cube
		smoothedCube = np.copy(cube)
		
		# Replace all NaNs with zero (and INFs with a finite number)
		if found_nan: smoothedCube = np.nan_to_num(smoothedCube)
		
		smoothedCube[(smoothedCube > 0) & (msk > 0)] = +maskScaleXY * rms
		smoothedCube[(smoothedCube < 0) & (msk > 0)] = -maskScaleXY * rms
		
		# Spatial smoothing
		if kx + ky:
			smoothedCube = ndimage.filters.gaussian_filter(smoothedCube, [0, ky / FWHM_CONST, kx / FWHM_CONST], mode=edgeMode)
		
		# Spectral smoothing
		if kz:
			if   kt == "b": smoothedCube = ndimage.filters.uniform_filter1d(smoothedCube, kz, axis=0, mode=edgeMode)
			elif kt == "g": smoothedCube = ndimage.filters.gaussian_filter1d(smoothedCube, kz / FWHM_CONST, axis=0, mode=edgeMode)
		
		# Re-insert the NaNs (but not the INFs) taken out earlier
		if found_nan: smoothedCube[np.isnan(cube)] = np.nan
		
		# Calculate the RMS of the smoothed cube:
		smoothedrms = GetRMS(smoothedCube, rmsMode=rmsMode, fluxRange=fluxRange, zoomx=1, zoomy=1, zoomz=1, verbose=verbose, sample=sampleRms)
		
		# Get rid of the NaNs a second time
		#if found_nan: smoothedCube = np.nan_to_num(smoothedCube)
		# NOTE: This should not be necessary because any comparison with NaN will always yield False.
		#       Hence, NaN pixels will never be included in the mask below.
		
		# Add pixels above threshold to mask by setting bit 1
		with np.errstate(invalid="ignore"):
			msk = np.bitwise_or(msk, np.greater_equal(np.absolute(smoothedCube), threshold * smoothedrms))
		
		# Delete smoothed cube again
		del smoothedCube
	return msk