Source code for sicor.Tools.majority_mask_filter

from numba import jit
import numpy as np
import functools


[docs] def _tuplify_majority_mask_filter(in_function): """ Make sure that "block_replace_params" is tuple of tuple in order to keep jit alive, where python lists are not jey implemented. """ @functools.wraps(in_function) def out_function(*args, **kwargs): if "block_replace_params" in kwargs: kwargs["block_replace_params"] = tuple([tuple(i) for i in kwargs["block_replace_params"]]) return in_function(*args, **kwargs) return out_function
[docs] @_tuplify_majority_mask_filter # ensure that [block_replace_params] is tuple of tuples @jit(nopython=True) def majority_mask_filter(mask, majority_width=2, block_replace=True, block_replace_params=((50, 5),), nodata_value=255): """ Post processing of noisy masks of integer values. The set of values in mask are the same as in the result. Two filters are performed: 1.) Majority filter: Take a window of size [majority_width] around each pixel (i,j) and replace the the local value with the most occuring one within that window. The nodata value is not taken. Filterung takes place if [majority_width] is larger than zero. 2.) Block replace: If the pixel (i,j) is within [block_replace_params=((([id of classs],[area to be filled up]),)] then replace all pixels in a window of range [area to be filled up] with [id of classs]. The order is taken from the provided tuple ot list. :param nodata_value: for mask, should be integer :param mask: 2D numpy array of integer type :param majority_width: with of the 2D area for the majority search :param block_replace: whether to do a block replace :param block_replace_params: tuple of 2 parameters: (([id of class],[area to be filled up]),) :return: filtered mask """ # set the ranges to loop over the mask i_min = 0 j_min = 0 i_max = mask.shape[0] j_max = mask.shape[1] # enable / disable filtering filter_2d = True if majority_width > 0 else False # initialize result res = np.empty((i_max, j_max), dtype=np.uint8) # start loop for i in np.arange(i_min, i_max): for j in np.arange(j_min, j_max): if mask[i, j] == nodata_value: # copy nodata value areas res[i, j] = nodata_value else: if filter_2d is True: # make sure to stay within bounds of array i1, i2 = i - majority_width, i + majority_width if i1 < i_min: i1 = i_min if i2 > i_max: i2 = i_max j1, j2 = j - majority_width, j + majority_width if j1 < j_min: j1 = j_min if j2 > j_max: j2 = j_max # select most occurring value within window as new value bc = np.bincount(mask[i1:i2, j1:j2].flatten()) res[i, j] = bc.argmax() if res[i, j] == nodata_value: # is nodata, then edge if area is concerned -> take next best one bc[nodata_value] = 0 res[i, j] = bc.argmax() else: # with no filtering, copy array res[i, j] = mask[i, j] if block_replace is True: for mask_id, dd_mask in block_replace_params: # loop over provided set if res[i, j] == mask_id: # ensure to stay in range of the area i1, i2 = i - dd_mask, i + dd_mask if i1 < i_min: i1 = i_min if i2 > i_max: i2 = i_max j1, j2 = j - dd_mask, j + dd_mask if j1 < j_min: j1 = j_min if j2 > j_max: j2 = j_max # fill up area with value -> this can leak to nodata value areas res[i1:i2, j1:j2] = mask_id if block_replace is True: # leaking into nodata value areas might have occurred -> clean up for i in np.arange(i_min, i_max): for j in np.arange(j_min, j_max): if mask[i, j] == nodata_value: res[i, j] = nodata_value return res