When we obtain point cloud data from scanning equipment, the data will contain noise and artifacts. The noise characteristics of the point cloud include unreal points, isolated points, and irregularities. The device is removed based on the noise characteristics;
Statistical Filtering Statistical Outlier Removal (SOR) (removal of outliers)
-
The idea of filtering
is to perform a statistical analysis on the neighborhood of each point, and calculate the average distance from it to all adjacent points. Assuming that the obtained result is a Gaussian distribution whose shape is determined by the mean and standard deviation, then points whose average distance is outside the standard range (defined by the global distance mean and variance) can be defined as outliers and obtained from the data removed.
remove_statistical_outlier(self, nb_neighbors, std_ratio, print_progress=False)
When the average distance between the judgment point and the nb_neighbors neighboring points is greater than [average distance + std_ratio*σ], it is judged as a noise point, and std_ratio=2 or 3 is generally taken as the limit error;
- test
import open3d as o3d
# 加载点云
pcd = o3d.io.read_point_cloud("./data/desk.pcd")
# 统计滤波
k = 20 # K邻域点的个数
μ = 2.0 # 标准差乘数
sor_pcd, idx = pcd.remove_statistical_outlier(k, μ)#当判断点的k近邻的平均距离大于【平均距离+μ*σ】,即判定为噪声点,一般取μ=2或3为极限误差
sor_pcd.paint_uniform_color([0, 0, 1])
# 提取噪声点云
sor_noise_pcd = pcd.select_by_index(idx, invert=True)
sor_noise_pcd.paint_uniform_color([1, 0, 0])
o3d.visualization.draw_geometries([sor_pcd,sor_noise_pcd], window_name="SOR")
Radius Outier Removal
-
The filtering idea
traverses all points of the point cloud after the threshold parameter MinPts is given. For any point in the point cloud, suppose there are K points within its radius R. When K<MinPts, the point can be identified as a noise point, and the Click Remove.
remove_radius_outlier(self, nb_points, radius, print_progress=False)
Among them, nb_points: the minimum number of points in the neighborhood sphere, less than this number is a noise point;
radius: the radius of the neighborhood;
when the average distance of nb_points neighbors of the judgment point is greater than [average distance + μ*σ], it is judged as noise point;
- test
import open3d as o3d
# 加载点云
pcd = o3d.io.read_point_cloud("./data/desk.pcd")
# 半径滤波
MinPts = 5 # 邻域球内的最少点个数,小于该个数为噪声点
R = 0.05 # 邻域半径大小
# pc 去噪后的点云
# idx 去噪保留的点索引
pc, idx = pcd.remove_radius_outlier(MinPts, R)
pc.paint_uniform_color([0, 0, 1])
ror_noise_pcd = pcd.select_by_index(idx,invert = True)
ror_noise_pcd.paint_uniform_color([1, 0, 0])
o3d.visualization.draw_geometries([pc, ror_noise_pcd], window_name="半径滤波")
Guide filtering Guilter Filter
- Filtering idea
guided filtering assumes that the point cloud has a good function of retaining edge information after a linear transformation; Guided Filter is generally used to perform noise reduction and other processing on 2D images. In fact, 3D point cloud can be denoised after a slight modification . The Guided Filter algorithm for point clouds can be summarized as
-
Calculate the field N ( i ) N ( i ) of a certain point pi in the point cloudN(i);
-
N ( i ) N ( i )The mean ui u_iof all points in N ( i )uiand covariance Σ i Σ_iSi;
-
Calculate A k A_k according to the formulaAkand bk b_kbk;
-
q i = A k p i + b k q_i =A_k p_i +b_k qi=Akpi+bk, output qi q_iqias a pair of points pi p_ipiThe filtering result;
- python source code
- test
import numpy as np
import open3d as o3d
#guild filter
def guided_filter(pcd, radius, epsilon):
kdtree = o3d.geometry.KDTreeFlann(pcd)
points_copy = np.array(pcd.points)
points = np.asarray(pcd.points)
num_points = len(pcd.points)
for i in range(num_points):
k, idx, _ = kdtree.search_radius_vector_3d(pcd.points[i], radius)
if k < 3:
continue
neighbors = points[idx, :]
mean = np.mean(neighbors, 0)
cov = np.cov(neighbors.T)
e = np.linalg.inv(cov + epsilon * np.eye(3))
A = cov @ e
b = mean - A @ mean
points_copy[i] = A @ points[i] + b
pcd.points = o3d.utility.Vector3dVector(points_copy)
#添加噪声
def add_noise(pcd, sigma):
points = np.asarray(pcd.points)
noise = sigma * np.random.randn(points.shape[0], points.shape[1])
points += noise
pcd = o3d.io.read_point_cloud('./data/bunny.ply')
add_noise(pcd, 0.004)
o3d.visualization.draw_geometries([pcd],window_name="rawPointCloud")
guided_filter(pcd, 0.01, 0.1)
guided_filter(pcd, 0.01, 0.1)
o3d.visualization.draw_geometries([pcd],window_name="guildFilter")