Esempio n. 1
0
def gauss_fltr_pyramid(dem, size=None, full=False, origmask=False):
    """Pyaramidal downsampling approach for gaussian smoothing
    Avoids the need for large kernels, very fast
    Needs testing
    """
    dem = malib.checkma(dem)
    levels = int(np.floor(np.log2(size)))
    #print levels
    dim = (np.floor(np.array(dem.shape) / float(2**levels) + 1) *
           (2**levels)).astype(int)
    #print dem.shape
    #print dim
    #Can do something with np.pad here
    #np.pad(a_fp.filled(), 1, mode='constant', constant_values=(a_fp.fill_value,))
    dem2 = np.full(dim, dem.fill_value)
    offset = np.floor((dim - np.array(dem.shape)) / 2.0).astype(int)
    #print offset
    #dem2[0:dem.shape[0],0:dem.shape[1]] = dem.data
    dem2[offset[0]:dem.shape[0] + offset[0],
         offset[1]:dem.shape[1] + offset[1]] = dem.data
    dem2 = np.ma.masked_equal(dem2, dem.fill_value)
    #dem2 = dem
    for n in range(levels):
        print(dem2.shape)
        dim = (np.floor(np.array(dem2.shape) / 2.0 + 1) * 2).astype(int)
        #dem2 = gauss_fltr_astropy(dem2, size=5, origmask=origmask)
        #dem2 = gauss_fltr_astropy(dem2, size=5)
        dem2 = gauss_fltr_astropy(dem2, size=5)
        #Note: Should use zoom with same bilinear interpolation here for consistency
        #However, this doesn't respect nan
        #dem2 = zoom(dem2, 0.5, order=1, prefilter=False, cval=dem.fill_value)
        dem2 = dem2[::2, ::2]
    if full:
        print("Resizing to original input dimensions")
        from scipy.ndimage import zoom
        for n in range(levels):
            print(dem2.shape)
            #Note: order 1 is bilinear
            dem2 = zoom(dem2, 2, order=1, prefilter=False, cval=dem.fill_value)
        #dem2 = zoom(dem2, 2**levels, order=1, prefilter=False, cval=dem2.fill_value)
        print(dem2.shape)
        #This was for power of 2 offset
        #offset = (2**levels)/2
        #print offset
        #dem2 = dem2[offset:dem.shape[0]+offset,offset:dem.shape[1]+offset]
        #Use original offset
        dem2 = dem2[offset[0]:dem.shape[0] + offset[0],
                    offset[1]:dem.shape[1] + offset[1]]
        if origmask:
            print("Applying original mask")
            #Allow filling of interior holes, but use original outer edge
            maskfill = malib.maskfill(dem)
            #dem2 = np.ma.array(dem2, mask=np.ma.getmaskarray(dem))
            dem2 = np.ma.array(dem2, mask=maskfill, fill_value=dem.fill_value)
    return dem2
Esempio n. 2
0
def median_fltr_skimage(dem, radius=3, erode=1, origmask=False):
    """
    Older skimage.filter.median_filter

    This smooths, removes noise and fills in nodata areas with median of valid pixels!  Effectively an inpainting routine
    """
    #Note, ndimage doesn't properly handle ma - convert to nan
    dem = malib.checkma(dem)
    dem = dem.astype(np.float64)
    #Mask islands
    if erode > 0:
        print("Eroding islands smaller than %s pixels" % (erode * 2)) 
        dem = malib.mask_islands(dem, iterations=erode)
    print("Applying median filter with radius %s" % radius) 
    #Note: this funcitonality was present in scikit-image 0.9.3
    import skimage.filter
    dem_filt_med = skimage.filter.median_filter(dem, radius, mask=~dem.mask)
    #Starting in version 0.10.0, this is the new filter
    #This is the new filter, but only supports uint8 or unit16
    #import skimage.filters
    #import skimage.morphology 
    #dem_filt_med = skimage.filters.rank.median(dem, disk(radius), mask=~dem.mask)
    #dem_filt_med = skimage.filters.median(dem, skimage.morphology.disk(radius), mask=~dem.mask)
    #Now mask all nans
    #skimage assigns the minimum value as nodata
    #CHECK THIS, seems pretty hacky
    #Also, looks like some valid values are masked at this stage, even though they should be above min
    ndv = np.min(dem_filt_med)
    #ndv = dem_filt_med.min() + 0.001
    out = np.ma.masked_less_equal(dem_filt_med, ndv)
    #Should probably replace the ndv with original ndv
    out.set_fill_value(dem.fill_value)
    if origmask:
        print("Applying original mask")
        #Allow filling of interior holes, but use original outer edge
        #maskfill = malib.maskfill(dem, iterations=radius)
        maskfill = malib.maskfill(dem)
        #dem_filt_gauss = np.ma.array(dem_filt_gauss, mask=dem.mask, fill_value=dem.fill_value)
        out = np.ma.array(out, mask=maskfill, fill_value=dem.fill_value)
    return out
Esempio n. 3
0
def gauss_fltr_astropy(dem, size=None, sigma=None, origmask=False, fill_interior=False):
    """Astropy gaussian filter properly handles convolution with NaN

    http://stackoverflow.com/questions/23832852/by-which-measures-should-i-set-the-size-of-my-gaussian-filter-in-matlab

    width1 = 3; sigma1 = (width1-1) / 6;
    Specify width for smallest feature of interest and determine sigma appropriately

    sigma is width of 1 std in pixels (not multiplier)

    scipy and astropy both use cutoff of 4*sigma on either side of kernel - 99.994%

    3*sigma on either side of kernel - 99.7%

    If sigma is specified, filter width will be a multiple of 8 times sigma 

    Alternatively, specify filter size, then compute sigma: sigma = (size - 1) / 8.

    If size is < the required width for 6-8 sigma, need to use different mode to create kernel

    mode 'oversample' and 'center' are essentially identical for sigma 1, but very different for sigma 0.3

    The sigma/size calculations below should work for non-integer sigma
    """

    #import astropy.nddata
    import astropy.convolution
    dem = malib.checkma(dem)
    #Generate 2D gaussian kernel for input sigma and size
    #Default size is 8*sigma in x and y directions
    #kernel = astropy.nddata.make_kernel([size, size], sigma, 'gaussian')
    #Size must be odd
    if size is not None:
        size = int(np.floor(size/2)*2 + 1)
        size = max(size, 3)
    #Truncate the filter at this many standard deviations. Default is 4.0
    truncate = 3.0
    if size is not None and sigma is None:
        sigma = (size - 1) / (2*truncate)
    elif size is None and sigma is not None:
        #Round up to nearest odd int
        size = int(np.ceil((sigma * (2*truncate) + 1)/2)*2 - 1)
    elif size is None and sigma is None:
        #Use default parameters
        sigma = 1
        size = int(np.ceil((sigma * (2*truncate) + 1)/2)*2 - 1)
    size = max(size, 3)
    kernel = astropy.convolution.Gaussian2DKernel(sigma, x_size=size, y_size=size, mode='oversample')

    print("Applying gaussian smoothing filter with size %i and sigma %0.3f (sum %0.3f)" % \
            (size, sigma, kernel.array.sum()))

    #This will fill holes
    #np.nan is float
    #dem_filt_gauss = astropy.nddata.convolve(dem.astype(float).filled(np.nan), kernel, boundary='fill', fill_value=np.nan)
    #dem_filt_gauss = astropy.convolution.convolve(dem.astype(float).filled(np.nan), kernel, boundary='fill', fill_value=np.nan)
    #Added normalization to ensure filtered values are not brightened/darkened if kernelsum != 1
    dem_filt_gauss = astropy.convolution.convolve(dem.astype(float).filled(np.nan), kernel, boundary='fill', fill_value=np.nan, normalize_kernel=True)
    #This will preserve original ndv pixels, applying original mask after filtering
    if origmask:
        print("Applying original mask")
        #Allow filling of interior holes, but use original outer edge
        if fill_interior:
            mask = malib.maskfill(dem)
        else:
            mask = dem.mask
        dem_filt_gauss = np.ma.array(dem_filt_gauss, mask=mask, fill_value=dem.fill_value)
    out = np.ma.fix_invalid(dem_filt_gauss, copy=False, fill_value=dem.fill_value)
    out.set_fill_value(dem.fill_value.astype(dem.dtype))
    return out.astype(dem.dtype)