Python 三维点云中的平面拟合
我正在尝试使用回归公式在三维点云中查找平面 我实施了最小二乘法和ransac解决方案, 但三参数方程将平面拟合限制为2.5D-该公式不能应用于平行于Z轴的平面 我的问题是如何将平面拟合推广到全3d? 我想添加第四个参数,以得到完整的方程 aX+bY+c*Z+d 我如何避免平凡的(0,0,0,0)解决方案 谢谢 我正在使用的代码:Python 三维点云中的平面拟合,python,3d,geometry,computer-vision,point-clouds,Python,3d,Geometry,Computer Vision,Point Clouds,我正在尝试使用回归公式在三维点云中查找平面 我实施了最小二乘法和ransac解决方案, 但三参数方程将平面拟合限制为2.5D-该公式不能应用于平行于Z轴的平面 我的问题是如何将平面拟合推广到全3d? 我想添加第四个参数,以得到完整的方程 aX+bY+c*Z+d 我如何避免平凡的(0,0,0,0)解决方案 谢谢 我正在使用的代码: from sklearn import linear_model def local_regression_plane_ransac(neighborhood):
from sklearn import linear_model
def local_regression_plane_ransac(neighborhood):
"""
Computes parameters for a local regression plane using RANSAC
"""
XY = neighborhood[:,:2]
Z = neighborhood[:,2]
ransac = linear_model.RANSACRegressor(
linear_model.LinearRegression(),
residual_threshold=0.1
)
ransac.fit(XY, Z)
inlier_mask = ransac.inlier_mask_
coeff = model_ransac.estimator_.coef_
intercept = model_ransac.estimator_.intercept_
更新
此功能现在集成在中,使平面拟合过程更加简单:
给定一个点云:
您只需添加如下标量字段:
is_floor = cloud.add_scalar_field("plane_fit")
Wich将为拟合平面的点添加一个值为1的新列
可以可视化标量字段:
旧答案 我认为你可以很容易地使用拟合平面的三维点,而不是回归 以下是一个简单的PCA实现:
def PCA(data, correlation = False, sort = True):
""" Applies Principal Component Analysis to the data
Parameters
----------
data: array
The array containing the data. The array must have NxM dimensions, where each
of the N rows represents a different individual record and each of the M columns
represents a different variable recorded for that individual record.
array([
[V11, ... , V1m],
...,
[Vn1, ... , Vnm]])
correlation(Optional) : bool
Set the type of matrix to be computed (see Notes):
If True compute the correlation matrix.
If False(Default) compute the covariance matrix.
sort(Optional) : bool
Set the order that the eigenvalues/vectors will have
If True(Default) they will be sorted (from higher value to less).
If False they won't.
Returns
-------
eigenvalues: (1,M) array
The eigenvalues of the corresponding matrix.
eigenvector: (M,M) array
The eigenvectors of the corresponding matrix.
Notes
-----
The correlation matrix is a better choice when there are different magnitudes
representing the M variables. Use covariance matrix in other cases.
"""
mean = np.mean(data, axis=0)
data_adjust = data - mean
#: the data is transposed due to np.cov/corrcoef syntax
if correlation:
matrix = np.corrcoef(data_adjust.T)
else:
matrix = np.cov(data_adjust.T)
eigenvalues, eigenvectors = np.linalg.eig(matrix)
if sort:
#: sort eigenvalues and eigenvectors
sort = eigenvalues.argsort()[::-1]
eigenvalues = eigenvalues[sort]
eigenvectors = eigenvectors[:,sort]
return eigenvalues, eigenvectors
下面是如何将点拟合到平面:
def best_fitting_plane(points, equation=False):
""" Computes the best fitting plane of the given points
Parameters
----------
points: array
The x,y,z coordinates corresponding to the points from which we want
to define the best fitting plane. Expected format:
array([
[x1,y1,z1],
...,
[xn,yn,zn]])
equation(Optional) : bool
Set the oputput plane format:
If True return the a,b,c,d coefficients of the plane.
If False(Default) return 1 Point and 1 Normal vector.
Returns
-------
a, b, c, d : float
The coefficients solving the plane equation.
or
point, normal: array
The plane defined by 1 Point and 1 Normal vector. With format:
array([Px,Py,Pz]), array([Nx,Ny,Nz])
"""
w, v = PCA(points)
#: the normal of the plane is the last eigenvector
normal = v[:,2]
#: get a point from the plane
point = np.mean(points, axis=0)
if equation:
a, b, c = normal
d = -(np.dot(normal, point))
return a, b, c, d
else:
return point, normal
然而,由于该方法对异常值敏感,您可以使用它使拟合对异常值具有鲁棒性
有一个ransac的Python实现
您只需要定义一个平面模型类,就可以使用它将平面拟合到三维点
在任何情况下,如果您可以清除异常值中的3D点(也许您可以使用KD Tree S.O.R过滤器),您应该可以使用PCA获得非常好的结果
以下是一个应用程序的实现:
def统计输出删除(kdtree,k=8,z_max=2):
“”“计算给定KDTree上的统计异常值删除筛选器。
参数
----------
kdtree:scipy的kdtree实例
KDTree的结构将用于
计算过滤器。
k(可选):int
最近邻的数量wich将用于估计
从每个点到其最近邻居的平均距离。
默认值:8
z_max(可选):int
最大Z分数决定该点是异常值还是异常值
不
退换商品
-------
sor_过滤器:布尔数组
布尔掩码,指示应保留或不保留点的位置。
布尔掩码的大小将与点数相同
在树上。
笔记
-----
应使用两个可选参数(k和z_max)进行调整
将过滤器设置为所需的结果。
较高的“k”值(通常)将导致修剪的点数增加。
较低的“z_max”值(通常)将导致修剪的点数增加。
"""
距离,i=kdtree.query(kdtree.data,k=k,n_jobs=-1)
z_距离=统计值zscore(np.平均值(距离,轴=1))
sor_过滤器=abs(z_距离)
您可以为函数提供一个KDtree,其中包含可能使用
您需要先安装。请随意使用这些参数。这里的点是一个包含n个3d点的nx3 numpy阵列。模型将是[a,b,c,d],这样ax+by+cz+d=0您可以添加(a,b,c)必须规范化的约束。谢谢,您能解释更多关于如何应用约束的内容吗?我从未使用过sklearn,但我确信它应该在某个地方有选择。否则我知道scipy.optimize有一些很好的例程可以做到这一点。或者你可以通过使用极角直接插入norm=1,即(a,b,c)=(cosθ,sinθ*cos phi,sinθ*sin phi)和fit over(θ,phi,d)。如果你只需要一个解,你可以运行fit两次。第一次通常像现在一样,第二次在Z和X之间更改轴,然后再次运行。第二次,如果你得到一个穿过x轴的平面,这意味着在你的原始数据中,你有一个穿过z轴的平面。这不是很优雅,但应该可以工作。您好,所以您写道:is_floor=cloud。假设点云中只有一个平面,则添加标量场(“平面拟合”),对吗?如果你有几架飞机呢?例如,如果你把两只恐龙放在一个盒子/房间里等,你会有6架飞机。你能写出相应的代码来提取6个平面吗?这是Jupyter笔记本中的3.js吗?
def statistical_outilier_removal(kdtree, k=8, z_max=2 ):
""" Compute a Statistical Outlier Removal filter on the given KDTree.
Parameters
----------
kdtree: scipy's KDTree instance
The KDTree's structure which will be used to
compute the filter.
k(Optional): int
The number of nearest neighbors wich will be used to estimate the
mean distance from each point to his nearest neighbors.
Default : 8
z_max(Optional): int
The maximum Z score wich determines if the point is an outlier or
not.
Returns
-------
sor_filter : boolean array
The boolean mask indicating wherever a point should be keeped or not.
The size of the boolean mask will be the same as the number of points
in the KDTree.
Notes
-----
The 2 optional parameters (k and z_max) should be used in order to adjust
the filter to the desired result.
A HIGHER 'k' value will result(normally) in a HIGHER number of points trimmed.
A LOWER 'z_max' value will result(normally) in a HIGHER number of points trimmed.
"""
distances, i = kdtree.query(kdtree.data, k=k, n_jobs=-1)
z_distances = stats.zscore(np.mean(distances, axis=1))
sor_filter = abs(z_distances) < z_max
return sor_filter
import pcl
cloud = pcl.PointCloud()
cloud.from_array(points)
seg = cloud.make_segmenter_normals(ksearch=50)
seg.set_optimize_coefficients(True)
seg.set_model_type(pcl.SACMODEL_PLANE)
seg.set_normal_distance_weight(0.05)
seg.set_method_type(pcl.SAC_RANSAC)
seg.set_max_iterations(100)
seg.set_distance_threshold(0.005)
inliers, model = seg.segment()