点云梯度下采样

这篇具有很好参考价值的文章主要介绍了点云梯度下采样。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

点云下采样又称点云精简。

均匀网格下采样

均匀网格下采样法是建立在空间包围盒精简算法之上对散乱点云快速简化的一种算法,其基本思想为:根据点云数据的密度确定最小三维网格(体素)的边长为 a ∗ b ∗ c a*b*c abc,计算最小三维网格的重心,通过邻域搜索保留离重心点最近的点并删除其余的点。每个三维网格重心依据式下式进行计算。
X = ∑ i = 1 n x i n , Y = ∑ i = 1 n y i n , Z = ∑ i = 1 n z i n X=\frac{\sum_{i=1}^n x_i}{n}, \quad Y=\frac{\sum_{i=1}^n y_i}{n}, \quad Z=\frac{\sum_{i=1}^n z_i}{n} X=ni=1nxi,Y=ni=1nyi,Z=ni=1nzi
其中 n n n 表示最小三维网格中的点云数据量 [ 1 ] ^{[1]} [1]

原理类似与点云降采样(DownSampling这篇中所提到的体素网格下采样。

曲率下采样

对于上述的均匀下采样(体素下采样),随着体素尺寸的增大,采样后得到的点云将会丢失细节特征,如曲率较大处,相反,如果在曲率变化不大的地方采样过多点,会显得冗余。所以,在这种情况下基于曲率特征的点云采样方法更加合适。

采样思路如下:

Step1:使用局部曲面拟合法计算出每个待采样点 p i p_i pi 的曲率 H i H_i Hi,并计算点云整体的平均曲率作为曲率阈值 H t H_t Ht

Step2:比较 H i H_i Hi 与曲率阈值 H t H_t Ht 的大小,如果小于曲率阈值 H t H_t Ht,则把采样点 p i p_i pi 划分到平缓区域,反之划分到陡峭区域。
Step3:采用均匀网格法对两个区域的点云进行精简(下采样),陡峭区域和平缓区域的边长阈值分别设置为 A A A B B B ,并且 A < B A<B A<B [ 1 ] ^{[1]} [1]

梯度下采样

通过类比法,我们用有限元体网格节点梯度替代表面曲率,设计出一种基于梯度特征的节点点云下采样方法。

采样思路如下:

Step1:计算出每个待采样点 p i p_i pi 的梯度 G i G_i Gi,并计算节点点云整体的平均梯度作为梯度阈值 G t G_t Gt

Step2:比较 G i G_i Gi 与梯度阈值 G t G_t Gt 大小,如果小于梯度阈值 G t G_t Gt,则把采样点 p i p_i pi 划分到节点属性变化剧烈区域,反之划分到节点属性变化缓慢区域。
Step3:采用均匀网格法对两个区域的节点点云进行精简(下采样),剧烈区域和缓慢区域的边长阈值分别设置为 A A A B B B ,并且 A < B A<B A<B

代码实现

均匀网格下采样

均匀网格下采样,也就是体素下采样的python代码实现如下:

def voxel_filter(origin_points, leaf_size):
    """体素下采样"""
    filtered_points = []
    # 计算边界点
    x_min, y_min, z_min = np.amin(origin_points, axis = 0)  # 计算x y z 三个维度的最值
    x_max, y_max, z_max = np.amax(origin_points, axis = 0)

    # 计算 voxel grid维度
    Dx = (x_max - x_min) // leaf_size + 1
    Dy = (y_max - y_min) // leaf_size + 1
    Dz = (z_max - z_min) // leaf_size + 1
    # print("Dx x Dy x Dz is {} x {} x {}".format(Dx, Dy, Dz))

    # 计算每个点的voxel索引,即确定每个点所被划分到的voxel
    h = []  # h 为保存索引的列表
    for i in range(len(origin_points)):
        hx = (origin_points[i][0] - x_min) // leaf_size
        hy = (origin_points[i][1] - y_min) // leaf_size
        hz = (origin_points[i][2] - z_min) // leaf_size
        h.append(hx + hy * Dx + hz * Dx * Dy)  # voxel索引填充顺序x-y-z
    h = np.array(h)

    # 筛选点
    h_indice = np.argsort(h)  # 返回h里面的元素按从小到大排序的索引
    h_sorted = h[h_indice]
    begin = 0
    for i in range(len(h_sorted)):
        if i == len(h_sorted) - 1:  # 到最后一个体素的最后一个点
            point_idx = h_indice[begin: i + 1]
            filtered_points.append(np.mean(origin_points[point_idx], axis = 0))  # 计算最后一个体素的采样点
            continue
        if h_sorted[i] == h_sorted[i + 1]:
            continue
        else:
            point_idx = h_indice[begin: i + 1]
            filtered_points.append(np.mean(origin_points[point_idx], axis = 0))
            begin = i + 1

    # 把点云格式改成array,并对外返回
    filtered_points = np.array(filtered_points, dtype = np.float64)
    return filtered_points

上面代码参考这篇文章【点云学习】Python实现点云体素下采样(Voxel Filter)中的代码。原文中的代码在迭代采样体素过程中,存在无法在最后一个体素中采样的问题。所以,我在迭代中添加了如下语句,进行完善。

    if i == len(h_sorted) - 1:  # 到最后一个体素的最后一个点
        point_idx = h_indice[begin: i + 1]
        filtered_points.append(np.mean(origin_points[point_idx], axis = 0))  # 计算最后一个体素的采样点
        continue

梯度下采样

结合上述的算法流程,梯度下采样python代码实现如下。

def gradient_downsampling(origin_points, G, a, b):
    """gradient downsampling

    :param origin_points: 源点云
    :param G: 点云梯度值
    :param a: 梯度变化剧烈区域采样体素尺寸
    :param b: 梯度变化缓慢区域采样体素尺寸
    :return: 采样点云
    """
    filtered_points, a_points, b_points = [], [], []
    # 将点与其对应梯度绑定
    # origin_points = np.hstack((origin_points, G.reshape(-1, 1)))
    # Step1: 将平均梯度作为梯度阈值
    G_t = np.mean(G)
    # Step2: 根据梯度划分点云为a_points和b_points两个区域
    for i, G_i in enumerate(G):
        if G_i > G_t:
            a_points.append(origin_points[i])
        else:
            b_points.append(origin_points[i])

    # Step3: 采样体素下采样对a_points和b_points两个区域进行采样
    a_filtered = voxel_filter(np.array(a_points), a, near = True)
    b_filtered = voxel_filter(np.array(b_points), b, near = True)
    filtered_points = np.vstack((a_filtered, b_filtered))

    return filtered_points

为了满足采样点为源点云中的点,即采样距离体素重心最近的点,改写voxel_filter函数如下。

def voxel_filter(origin_points, leaf_size, near = False):
    """体素下采样"""
    filtered_points = []
    if near:
        # 构建KD-Tree寻找最近点
        from scipy import spatial
        tree = spatial.KDTree(data = origin_points[:, :3])

    # 计算边界点
    x_min, y_min, z_min = np.amin(origin_points[:, :3], axis = 0)  # 计算x y z 三个维度的最值
    x_max, y_max, z_max = np.amax(origin_points[:, :3], axis = 0)

    # 计算 voxel grid维度
    Dx = (x_max - x_min) // leaf_size + 1
    Dy = (y_max - y_min) // leaf_size + 1
    Dz = (z_max - z_min) // leaf_size + 1
    # print("Dx x Dy x Dz is {} x {} x {}".format(Dx, Dy, Dz))

    # 计算每个点的voxel索引,即确定每个点所被划分到的voxel
    h = []  # h 为保存索引的列表
    for i in range(len(origin_points)):
        hx = (origin_points[i][0] - x_min) // leaf_size
        hy = (origin_points[i][1] - y_min) // leaf_size
        hz = (origin_points[i][2] - z_min) // leaf_size
        h.append(hx + hy * Dx + hz * Dx * Dy)  # voxel索引填充顺序x-y-z
    h = np.array(h)

    # 筛选点
    h_indice = np.argsort(h)  # 返回h里面的元素按从小到大排序的索引
    h_sorted = h[h_indice]
    begin = 0
    for i in range(len(h_sorted)):
        point_idx = h_indice[begin: i + 1]
        if i == len(h_sorted) - 1:  # 到最后一个体素的最后一个点
            if near:
                query_point = np.mean(origin_points[point_idx], axis = 0)[:3]
                dist, ind = tree.query(query_point, k = 1)
                filtered_points.append(origin_points[ind])
            else:
                filtered_points.append(np.mean(origin_points[point_idx], axis = 0))  # 计算最后一个体素的采样点
            continue
        if h_sorted[i] == h_sorted[i + 1]:
            continue
        else:
            if near:
                query_point = np.mean(origin_points[point_idx], axis = 0)[:3]
                dist, ind = tree.query(query_point, k = 1)
                filtered_points.append(origin_points[ind])
            else:
                filtered_points.append(np.mean(origin_points[point_idx], axis = 0))
            begin = i + 1

    # 把点云格式改成array,并对外返回
    filtered_points = np.array(filtered_points, dtype = np.float64)
    return filtered_points

通过构建待采样点云的KD-Tree实现最邻近搜索。

测试结果

我们对比测试同一待采样点云的体素下采样和梯度下采样,测试代码如下:

input_points = np.hstack((points, gradient))
drawPointCloud(input_points, color = True)
# 体素下采样
Vsample_points = voxel_filter(input_points, 5)
drawPointCloud(Vsample_points, color = True)
# 梯度下采样
Gsample_points = gradient_downsampling(input_points, gradient, 2, 5)
drawPointCloud(Gsample_points, color = True)

这里我们通过使用open3d库进行点云可视化。

def drawPointCloud(points, color = False):
    import open3d as o3d
    cloud = o3d.geometry.PointCloud()
    cloud.points = o3d.utility.Vector3dVector(points[:, :3])
    print(len(cloud.points))
    if not color:
        # 所有点统一颜色
        cloud.paint_uniform_color([241 / 255, 135 / 255, 184 / 255])
    else:
        # 颜色映射
        colors = np.zeros([points.shape[0], 3])
        color_max = np.max(points[:, 3])
        color_min = np.min(points[:, 3])
        delta_c = abs(color_max - color_min) / (255 * 2)
        for j in range(points.shape[0]):
            color_n = (points[:, 3][j] - color_min) / delta_c
            if color_n <= 255:
                colors[j, :] = [0, 1 - color_n / 255, 1]
            else:
                colors[j, :] = [(color_n - 255) / 255, 0, 1]

        cloud.colors = o3d.utility.Vector3dVector(colors)
    o3d.visualization.draw_geometries([cloud])

测试结果如下图所示。

点云梯度下采样

点云梯度下采样
点云梯度下采样

PinkCAx中测试结果如下。
点云梯度下采样

参考

[1] 李国远,梁周雁,石信肖,等. 基于曲率特征约束的激光点云精简方法研究[J]. 计算机与数字工程,2020,48(8):2034-2037,2063. DOI:10.3969/j.issn.1672-9722.2020.08.042.
[2] 【点云学习】Python实现点云体素下采样(Voxel Filter)
[3] 采用Open3d绘制高度颜色点云图文章来源地址https://www.toymoban.com/news/detail-417553.html

到了这里,关于点云梯度下采样的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 点云从入门到精通技术详解100篇-基于 LSTM 的点云降采样(下)

    目录 3.3 实验结果与分析 3.3.1 点云分类实验 3.3.2 点云检索实验 3.4 消融实验

    2024年02月07日
    浏览(43)
  • MATLAB 点云均匀体素下采样(6)

    不同参数调整下的均匀体素下采样结果如下图所示,后续代码复制黏贴即可: 分别为0.3m,0.2m,0.1m尺度下的格网下采样结果 均匀体素下采样方法。同一框中的点合并到输出中的单个点。它们的颜色和正常属性相应地取平均值。这种方法比“随机”下采样方法更好地保持了点

    2024年02月03日
    浏览(30)
  • 3D点云分割系列5:RandLA-Net:3D点云的实时语义分割,随机降采样的重生

    《RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds》发布于CVPR 2020。 在自动驾驶等领域,高效的分割网络是目前最基本和最关键的研究方向。目前存在的一些点云处理方法包括PointNet、PointNet++、PointCNN、KPConv等方法,或多或少都存在效率不高或是特征采样不足的情况,

    2024年02月04日
    浏览(53)
  • Open3D 降采样:让点云数据更加高效

    Open3D 降采样:让点云数据更加高效 点云数据处理是计算机视觉中重要的一项任务,而点云数据本身就非常庞大,需要消耗大量的计算资源进行处理。因此,点云数据的降采样是非常必要的。Open3D 是一个面向三维数据处理的开源库,提供了丰富的点云数据处理工具,其中包括

    2024年02月03日
    浏览(49)
  • 随机采样一致性(RANSAC)三维点云的平面拟合算法(含C++代码)

            随机采样一致性(Random sample consensus,RANSAC) :RANSAC是一种鲁棒的模型拟合方法,它可以处理存在大量噪声和异常值的数据。在进行平面拟合时,RANSAC会随机选择三个点,然后计算这三个点确定的平面模型。然后,RANSAC会计算其他所有点到这个平面的距离,并根据

    2024年02月07日
    浏览(42)
  • 元数据(Metadata),又称中介数据、中继数据

    元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。元数据算是一种电子式目录,为了达到编制目录的目的,必须在描述并收藏数据的内容或特色

    2024年02月03日
    浏览(73)
  • HyperBDR云容灾深度解析三:适配上云下云跨云多场景

    2022年信通院首次发布云容灾白皮书,这意味着随着企业上云加速,混合云场景增多,容灾上云方案逐步成为一些行业伙伴在考虑的新选择。本文从一些传统容灾应用出发,帮助大家普及云容灾的类型并拓展云容灾解决方案的应用场景。 在了解云容灾的应用场景之前,就得先

    2024年02月12日
    浏览(43)
  • 利用 Amazon EMR Serverless、Amazon Athena、Apache Dolphinscheduler 以及本地 TiDB 和 HDFS 在混合部署环境中构建无服务器数据仓库(一)云上云下数据同步方案设计

    在数据驱动的世界中,企业正在寻求可靠且高性能的解决方案来管理其不断增长的数据需求。本系列博客从一个重视数据安全和合规性的 B2C 金融科技客户的角度来讨论云上云下混合部署的情况下如何利用亚马逊云科技云原生服务、开源社区产品以及第三方工具构建无服务器

    2024年04月25日
    浏览(43)
  • PySide6精简教程

    近几年,受益于人工智能的崛起,Python语言几乎以压倒性优势在众多编程语言中异军突起,成为AI时代的首选语言。在很多情况下,我们想要以图形化方式将我们的人工智能算法打包提供给用户使用,这时候选择以python为主的GUI框架就非常合适了。 QT是众多GUI框架里面非常著

    2024年02月03日
    浏览(47)
  • RabbitMQ-精简版

    声明: 本博客已标明出处,如有侵权请告知,马上删除。 RabbitMQ是微服务间实现异步通讯的一种技术. RabbitMQ中的一些角色: publisher:生产者 consumer:消费者 exchange个:交换机,负责消息路由 queue:队列,存储消息 virtualHost:虚拟主机,隔离不同租户的exchange、queue、消息的隔

    2023年04月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包