Python 在三维点云中加速RANSAC平面检测

Python 在三维点云中加速RANSAC平面检测,python,plane,ransac,Python,Plane,Ransac,现在我正在使用RANSAC对3D点云数据进行平面分割。我的代码的基本流程图是: 选择3个随机点,然后创建候选平面 检查距平面一定距离阈值内的所有其他点。如果有许多点低于阈值,那么我保存平面。 如果覆盖点低于阈值,则选择另一个RANSAC点返回1号 让我沮丧的是,如何加快这个过程?想象一下,如果我有1百万个点,那么我必须检查候选平面上的所有点,如果不符合阈值,那么我选择另一个随机点,并用1百万个点反复检查。对于任何对我的代码的建议或修改,我将非常感谢 这是我的代码,数据集可以下载。对于这个示例,我

现在我正在使用RANSAC对3D点云数据进行平面分割。我的代码的基本流程图是:

选择3个随机点,然后创建候选平面 检查距平面一定距离阈值内的所有其他点。如果有许多点低于阈值,那么我保存平面。 如果覆盖点低于阈值,则选择另一个RANSAC点返回1号 让我沮丧的是,如何加快这个过程?想象一下,如果我有1百万个点,那么我必须检查候选平面上的所有点,如果不符合阈值,那么我选择另一个随机点,并用1百万个点反复检查。对于任何对我的代码的建议或修改,我将非常感谢

这是我的代码,数据集可以下载。对于这个示例,我使用了实际数据的一小部分

import numpy as np
import pandas as pd
import math
from scipy.spatial import cKDTree
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import random

data = pd.read_csv('data_sample.txt', usecols=[0,1,2], header=None, delimiter=' ')
df_points = pd.DataFrame(data)

%matplotlib qt


#Change parameter
distance_threshold = 0.06
minimum_point_threshold = 400
sampling_resolution = 0.06

#search nearest neighbor
kdtree = cKDTree(df_points)

temp_pair_point = []
list_pair_point = []
list_candidates = []

#Select random points
for h in df_points.index:
    pair_point = kdtree.query_ball_point(np.array(df_points.iloc[h]),r=sampling_resolution, return_sorted=False)
    temp_pair_point.append(pair_point)


for k in range(0,len(temp_pair_point)):
    pairsize = len(temp_pair_point[k])
    if pairsize > 10:
        list_pair_point.append(temp_pair_point[k])

for m in range(0,len(list_pair_point)):
    candidates = random.choices(list_pair_point[m], k=3)
    list_candidates.append(candidates)




#select random point from nearest neighbor
list_sample_point = []

for i in range(0,len(list_candidates)):
    list_test_distance = []


    p1 = np.array(data.iloc[list_candidates[i][0]])
    p2 = np.array(data.iloc[list_candidates[i][1]])
    p3 = np.array(data.iloc[list_candidates[i][2]])
    sample = [p1,p2,p3]

    #create plane
    x1,y1,z1 =np.ravel(p1)
    x2,y2,z2 =np.ravel(p2)
    x3,y3,z3 =np.ravel(p3)

    m_u = [x2-x1,y2-y1,z2-z1]
    m_v = [x3-x2,y3-y2,z3-z2]

    m_normal = np.cross(m_u,m_v)
    m_pos = p1
    m_dist = np.dot(-m_normal, m_pos)

    for j in data.index:
        test_point = np.array(data.iloc[j])
        test_distance = np.dot(-m_normal,test_point)
        #print('Distance ={}'.format(test_distance))
        if abs(test_distance) < distance_threshold:
            list_test_distance.append(test_distance)

    if len(list_test_distance) > minimum_point_threshold:
        list_sample_point.append(sample)



#Check candidate plane
list_u = []
list_v = []
list_normal = []
list_distance = []
list_min_max = []

for i in range(0,len(list_sample_point)):
    psample = list_sample_point[i]
    psample1 = np.array(psample[0])
    psample2 = np.array(psample[1])
    psample3 = np.array(psample[2])
    min_max = [float(min(np.transpose(psample)[0])), float(max(np.transpose(psample)[0])),float(min(np.transpose(psample)[1])), float(max(np.transpose(psample)[1])),float(min(np.transpose(psample)[2])), float(max(np.transpose(psample)[2]))]
    list_min_max.append(min_max)

    u = [psample2[0]-psample1[0], psample2[1]-psample1[1], psample2[2]-psample1[2]]
    v = [psample3[0]-psample2[0], psample3[1]-psample2[1], psample3[2]-psample2[2]]
    normal = np.cross(u,v)
    d = np.dot(-normal,psample1)

    list_u.append(u)
    list_v.append(v)
    list_normal.append(normal)
    list_distance.append(d)




#Plot candidate plane  
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.scatter3D(xs=plot_x, ys=plot_y, zs=plot_z, s=30, c='red')
ax.set_xlim3d(-25,-20)
ax.set_ylim3d(5,10)
ax.set_xlim3d(5,10)
ax.scatter3D(xs=df_points[0], ys=df_points[1], zs=df_points[2], s=1, c='green')

for j in range(0,len(list_normal)):
    if list_normal[j][2] != 0:
        print('Normal plane to Z axis')
        xx, yy = np.meshgrid(np.arange(list_min_max[j][0],list_min_max[j][1]+(list_min_max[j][1]-list_min_max[j][0]),(list_min_max[j][1]-list_min_max[j][0])), np.arange(list_min_max[j][2],list_min_max[j][3]+(list_min_max[j][3]-list_min_max[j][2]),(list_min_max[j][3]-list_min_max[j][2])), sparse=True)
        z = (-list_normal[j][0] * xx - list_normal[j][1] * yy -list_distance[j]) / list_normal[j][2]    
        ax.plot_surface(xx, yy, z, color=np.random.rand(3,), alpha=0.5)

    elif list_normal[j][0] != 0:
        print('Normal plane to X axis')
        yy, zz, = np.meshgrid(np.arange(list_min_max[j][2],list_min_max[j][3]+(list_min_max[j][3]-list_min_max[j][2]),(list_min_max[j][3]-list_min_max[j][2])), np.arange(list_min_max[j][4],list_min_max[j][5]+(list_min_max[j][5]-list_min_max[j][4]),(list_min_max[j][5]-list_min_max[j][4])), sparse=True)
        x = (-list_normal[j][1] * yy - list_normal[j][2] * zz -list_distance[j]) / list_normal[j][0]
        ax.plot_surface(x, yy, zz,  color=np.random.rand(3,), alpha=0.5)

    elif list_normal[j][1] !=0:
        print('Normal plane to Y axis')
        xx, zz, = np.meshgrid(np.arange(list_min_max[j][0],list_min_max[j][1]+(list_min_max[j][1]-list_min_max[j][0]),(list_min_max[j][1]-list_min_max[j][0])), np.arange(list_min_max[j][4],list_min_max[j][5]+(list_min_max[j][5]-list_min_max[j][4]),(list_min_max[j][5]-list_min_max[j][4])), sparse=True)
        y = (-list_normal[j][0] * xx - list_normal[j][2] * zz -list_distance[j]) / list_normal[j][1]
        ax.plot_surface(xx, y, zz, color=np.random.rand(3,), alpha=0.5)

首先,RANSAC并不是要在每个点上运行。使用您的代码,它可以计算出1945个点中的1729个平面。通常,在启动RANSAC之前,您会计算要运行RANSAC的迭代次数。这取决于预期平面的内点和离群点的数量:

k = log(1-p)/log(1-(1-e)^s)
p是您期望的概率,RANSAC导致无离群点平面,例如99.99%概率。e是异常值的百分比,例如,对于80%的异常值e=0.8。这将导致1147次迭代。然而,通常情况下,你应该能够假设较低的异常概率,比如说20%,因为你现在选择了邻居,突然间,只有13次迭代才能检测到概率为99.99%的单个平面,所以只有13次!而不是1729年!。如果你想找到100个平面,那么你的迭代次数仍然会减少25%

其次,如果您仍然希望在每一点上运行代码,则可以加快代码的某些部分: 首先,对于可以在一个循环中写入的内容,您使用两个for循环,并继续计算测试距离,即使lenlist_test_distance>minimum_point_threshold可能已经为真。通过将代码更改为:

#select random point from nearest neighbor
list_sample_point = []
list_u = []
list_v = []
list_normal = []
list_distance = []
list_min_max = []


for i in range(0,len(list_candidates)):
    list_test_distance = []


    p1 = np.array(data.iloc[list_candidates[i][0]])
    p2 = np.array(data.iloc[list_candidates[i][1]])
    p3 = np.array(data.iloc[list_candidates[i][2]])
    sample = [p1,p2,p3]

    #create plane
    x1,y1,z1 =np.ravel(p1)
    x2,y2,z2 =np.ravel(p2)
    x3,y3,z3 =np.ravel(p3)

    m_u = [x2-x1,y2-y1,z2-z1]
    m_v = [x3-x2,y3-y2,z3-z2]

    m_normal = np.cross(m_u,m_v)
    m_pos = p1
    m_dist = np.dot(-m_normal, m_pos)

    for j in data.index:
        test_point = np.array(data.iloc[j])
        test_distance = np.dot(-m_normal,test_point)
        #print('Distance ={}'.format(test_distance))
        if abs(test_distance) < distance_threshold:
            list_test_distance.append(test_distance)

        if len(list_test_distance) > minimum_point_threshold:
            #list_sample_point.append(sample) I think you don't need this one any more as well
            psample = sample
            psample1 = np.array(psample[0])
            psample2 = np.array(psample[1])
            psample3 = np.array(psample[2])
            min_max = [float(min(np.transpose(psample)[0])), float(max(np.transpose(psample)[0])),float(min(np.transpose(psample)[1])), float(max(np.transpose(psample)[1])),float(min(np.transpose(psample)[2])), float(max(np.transpose(psample)[2]))]
            list_min_max.append(min_max)

            u = [psample2[0]-psample1[0], psample2[1]-psample1[1], psample2[2]-psample1[2]]
            v = [psample3[0]-psample2[0], psample3[1]-psample2[1], psample3[2]-psample2[2]]
            normal = np.cross(u,v)
            d = np.dot(-normal,psample1)

            list_u.append(u)
            list_v.append(v)
            list_normal.append(normal)
            list_distance.append(d)
            break

我将笔记本电脑上的运行时间从7分11秒改为2分35秒,速度提高了2.5倍多。结果应该是一样的,如果我错了,请纠正我

非常感谢@LeoE的建议。我对您的第一个选项非常感兴趣,它可以从1729次减少到仅13次迭代。但是你能告诉我这个k=log1-p/log1-1-e^s函数应该放在哪一部分吗。另外,我也试过你的第二个选择,但是我没有看到任何一架飞机出现。我在应用你的代码时出错了吗?我把你的新代码放在上面我的现有代码中。哦,是的,我的错误,对不起,中断必须有一个缩进,并且必须是if语句的一部分,请参阅editAnd,关于你的第一个问题:它有一个原因,为什么算法被这样调用,它包括随机抽样;它是一种通过随机选择样本并生成一致性来查找匹配项的算法。如果你再读一读这个算法是如何工作的以及它是做什么的,那可能是最好的:Hi@LeoE,你确定要使用psample=list\u sample\u point[i]吗?因为我总是得到超出范围的错误索引,即使在我用[j]更改之后。啊,该死,我想我可能复制了某种融合代码,是的,这需要更改,请参见编辑