def radius_filter(arr, radius, min_points): """ Applies a radius search filter, which remove isolated points/clusters of points. Args: arr (array): Point cloud of shape n points x m dimensions to be filtered. radius (float): Search radius around each point to form a neighborhood. min_point (int): Minimum number of points in a neighborhood for it to be considered valid, i.e not filtered out. Returns: mask (array): Array of bools masking valid points as True and "noise" points as False. """ # Setting up neighborhood indices. indices = set_nbrs_rad(arr, arr, radius, return_dist=False) # Allocating array of neighborhood's sizes (one entry for each point in # arr). n_points = np.zeros(arr.shape[0], dtype=int) # Iterating over each entry in indices and calculating total number of # points. for i, id_ in enumerate(indices): n_points[i] = id_.shape[0] return n_points >= min_points
def detect_rad_nn(arr, rad): """ Calculates an average of number of neighbors based on a fixed radius around each point in a point cloud. Parameters ---------- arr : array Three-dimensional (m x n) array of a point cloud, where the coordinates are represented in the columns (n) and the points are represented in the rows (m). rad : float Radius distance to select neighboring points. Returns ------- mean_knn : int Average number of points inside a radius 'rad' around each point in 'arr'. """ # Performin Nearest Neighbors search for the whole point cloud. indices = set_nbrs_rad(arr, arr, rad, return_dist=False) # Counting number of points around each point in 'arr'. indices_len = [len(i) for i in indices] # Calculates a mean of all neighboring point counts. mean_knn = np.mean(indices_len).astype(int) return mean_knn
def plane_filter(arr, rad, threshold): """ Filters a point cloud based on its points planicity. Removes points that are part of a neighbohood with planar spatial arrangement (low curvature). Parameters ---------- arr : array Three-dimensional (m x n) array of a point cloud, where the coordinates are represented in the columns (n) and the points are represented in the rows (m). rad : float Search radius distance around each point. Used to describe local point arrangement. threshold : float Minimum curvature value for valid points. Returns ------- mask_plane : numpy.ndarray Boolean mask with valid points entries set as True. """ # Running NearestNeighborhood search around each point in arr. nbrs_idx = set_nbrs_rad(arr, arr, rad, False) # Calculating curvature for each point's neighborhood. c = curvature(arr, nbrs_idx) return c >= threshold
def detect_optimal_knn(arr, rad_lst=[0.1, 0.2, 0.3], sample_size=10000): """ Detects optimal values for knn in order to facilitate material separation. Parameters ---------- arr: array Set of 3D points. rad_lst: list Set of radius values to generate samples of neighborhoods. This is used to select points to calculate a number of neighboring points distribution from the point cloud. sample_size: int Number of points in arr to process in order to genrate a distribution. Returns ------- knn_lst: list Set of k-nearest neighbors values. """ # Generating sample indices. sids = np.random.choice(np.arange(arr.shape[0]), sample_size, replace=False) # Obtaining nearest neighbors' indices and distance for sampled points. # This process is done just once, with the largest value of radius in # rad_lst. Later on, it is possible to subsample indices by limiting # their distances for a smaller radius. dist, ids = set_nbrs_rad(arr, arr[sids], np.max(rad_lst), True) # Initializing empty list to store knn values. knn_lst = [] # Looping over each radius value. for r in rad_lst: # Counting number of points inside radius r. n_pts = [len(i[d <= r]) for i, d in zip(ids, dist)] # Binning n_pts into a histogram. y, x = np.histogram(n_pts) # Detecting peaks of accumulated points from n_pts. maxtab, mintab = peakdet(y, 100) maxtab = np.array(maxtab) # Appending knn values relative to peaks detected in n_pts. knn_lst.append(x[maxtab[:, 0]]) # Flattening nested lists into a final list of knn values. knn_lst = [i for j in knn_lst for i in j] return knn_lst
def dist_majority(arr_1, arr_2, **kwargs): """ Applies majority filter on two arrays. Args: arr_1 (array): n-dimensional array of points to filter. arr_2 (array): n-dimensional array of points to filter. **kwargs: knn, rad. knn (int or float): Number neighbors to select around each point in arr in order to apply the majority criteria. rad (int or float): Search radius arount each point in arr to select neighbors in order to apply the majority criteria. Returns: c_maj_1 (array): Boolean mask of filtered entries of same class as input 'arr_1'. c_maj_2 (array): Boolean mask of filtered entries of same class as input 'arr_2'. Raises: AssertionError: Raised if neither 'knn' or 'rad' arguments are passed with valid values (int or float). """ # Asserting input arguments are valid. assert ('knn' in kwargs.keys()) or ('rad' in kwargs.keys()), 'Please\ input a value for either "knn" or "rad".' if 'knn' in kwargs.keys(): assert (type(kwargs['knn']) == int) or (type(kwargs['knn']) == float), \ '"knn" variable must be of type int or float.' elif 'rad' in kwargs.keys(): assert (type(kwargs['rad']) == int) or (type(kwargs['rad']) == float), \ '"rad" variable must be of type int or float.' # Stacking the arrays from both classes to generate a combined array. arr = np.vstack((arr_1, arr_2)) # Generating the indices for the local subsets of points around all points # in the combined array. Function used is based upon the argument passed. if 'knn' in kwargs.keys(): dist, indices = set_nbrs_knn(arr, arr, kwargs['knn']) elif 'rad' in kwargs.keys(): dist, indices = set_nbrs_rad(arr, arr, kwargs['rad']) # Making sure indices has type int. indices = indices.astype(int) # Generating the class arrays from both classified arrays and combining # them into a single classes array (classes). class_1 = np.full(arr_1.shape[0], 1, dtype=np.int) class_2 = np.full(arr_2.shape[0], 2, dtype=np.int) classes = np.hstack((class_1, class_2)).T # Allocating output variable. c_maj = np.zeros(classes.shape) # Selecting subset of classes based on the neighborhood expressed by # indices. class_ = classes[indices] # Looping over all points in indices. for i in range(len(indices)): # Obtaining classe from indices i. c = class_[i, :] # Caculating accummulated distance for each class. d1 = np.sum(dist[i][c == 1]) d2 = np.sum(dist[i][c == 2]) # Checking which class has the highest distance and assigning it # to current index in c_maj. if d1 >= d2: c_maj[i] = 1 elif d1 < d2: c_maj[i] = 2 return c_maj == 1, c_maj == 2
def class_filter(arr_1, arr_2, target, **kwargs): """ Function to apply class filter on an array based on the combination of classed from both arrays (arr_1 and arr_2). Which array gets filtered is defined by ''target''. Args: arr_1 (array): n-dimensional array of points to filter. arr_2 (array): n-dimensional array of points to filter. target (int or float): number of the input array to filter. Valid values are 0 or 1. **kwargs: knn, rad. knn (int or float): Number neighbors to select around each point in arr in order to apply the majority criteria. rad (int or float): Search radius arount each point in arr to select neighbors in order to apply the majority criteria. Returns: c_maj_1 (array): Boolean mask of filtered entries of same class as input 'arr_1'. c_maj_2 (array): Boolean mask of filtered entries of same class as input 'arr_2'. Raises: AssertionError: Raised if neither 'knn' or 'rad' arguments are passed with valid values (int or float). AssertionError: Raised if 'target' variable is not an int or float with value 0 or 1. """ # Asserting input arguments are valid. assert ('knn' in kwargs.keys()) or ('rad' in kwargs.keys()), 'Please\ input a value for either "knn" or "rad".' if 'knn' in kwargs.keys(): assert (type(kwargs['knn']) == int) or (type(kwargs['knn']) == float), \ '"knn" variable must be of type int or float.' elif 'rad' in kwargs.keys(): assert (type(kwargs['rad']) == int) or (type(kwargs['rad']) == float), \ '"rad" variable must be of type int or float.' assert (type(target) == int) or (type(target) == float), '"target"\ variable must be of type int or float.' assert (target == 0) or (target == 1), '"target" variable must be either\ 0 or 1.' # Stacking the arrays from both classes to generate a combined array. arr = np.vstack((arr_1, arr_2)) # Generating the class arrays from both classified arrays and combining # them into a single classes array (classes). class_1 = np.full(arr_1.shape[0], 1, dtype=np.int) class_2 = np.full(arr_2.shape[0], 2, dtype=np.int) classes = np.hstack((class_1, class_2)).T # Generating the indices for the local subsets of points around all points # in the combined array. Function used is based upon the argument passed. if 'knn' in kwargs.keys(): indices = set_nbrs_knn(arr, arr, kwargs['knn'], return_dist=False) elif 'rad' in kwargs.keys(): indices = set_nbrs_rad(arr, arr, kwargs['rad'], return_dist=False) # Making sure indices has type int. indices = indices.astype(int) # Allocating output variable. c_maj = classes.copy() # Selecting subset of classes based on the neighborhood expressed by # indices. class_ = classes[indices] # Checking for the target class. target_idx = np.where(classes == target)[0] # Looping over the target points to filter. for i in target_idx: # Counting the number of occurrences of each value in the ith instance # of class_. count = np.bincount(class_[i, :]) # Appending the majority class into the output variable. c_maj[i] = count.argmax() return c_maj == 1, c_maj == 2
def array_majority(arr_1, arr_2, **kwargs): """ Applies majority filter on two arrays. Args: arr_1 (array): n-dimensional array of points to filter. arr_2 (array): n-dimensional array of points to filter. **kwargs: knn, rad. knn (int or float): Number neighbors to select around each point in arr in order to apply the majority criteria. rad (int or float): Search radius arount each point in arr to select neighbors in order to apply the majority criteria. Returns: c_maj_1 (array): Boolean mask of filtered entries of same class as input 'arr_1'. c_maj_2 (array): Boolean mask of filtered entries of same class as input 'arr_2'. Raises: AssertionError: Raised if neither 'knn' or 'rad' arguments are passed with valid values (int or float). """ # Asserting input arguments are valid. assert ('knn' in kwargs.keys()) or ('rad' in kwargs.keys()), 'Please\ input a value for either "knn" or "rad".' if 'knn' in kwargs.keys(): assert (type(kwargs['knn']) == int) or (type(kwargs['knn']) == float), \ '"knn" variable must be of type int or float.' elif 'rad' in kwargs.keys(): assert (type(kwargs['rad']) == int) or (type(kwargs['rad']) == float), \ '"rad" variable must be of type int or float.' # Stacking the arrays from both classes to generate a combined array. arr = np.vstack((arr_1, arr_2)) # Generating the indices for the local subsets of points around all points # in the combined array. Function used is based upon the argument passed. if 'knn' in kwargs.keys(): indices = set_nbrs_knn(arr, arr, kwargs['knn'], return_dist=False) elif 'rad' in kwargs.keys(): indices = set_nbrs_rad(arr, arr, kwargs['rad'], return_dist=False) # Making sure indices has type int. indices = indices.astype(int) # Generating the class arrays from both classified arrays and combining # them into a single classes array (classes). class_1 = np.full(arr_1.shape[0], 1, dtype=np.int) class_2 = np.full(arr_2.shape[0], 2, dtype=np.int) classes = np.hstack((class_1, class_2)).T # Allocating output variable. c_maj = np.zeros(classes.shape) # Selecting subset of classes based on the neighborhood expressed by # indices. class_ = classes[indices] # Looping over all points in indices. for i in range(len(indices)): # Counting the number of occurrences of each value in the ith instance # of class_. unique, count = np.unique(class_[i, :], return_counts=True) # Appending the majority class into the output variable. c_maj[i] = unique[np.argmax(count)] return c_maj == 1, c_maj == 2