点云梯度下采样

2023-11-01

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

均匀网格下采样

均匀网格下采样法是建立在空间包围盒精简算法之上对散乱点云快速简化的一种算法,其基本思想为:根据点云数据的密度确定最小三维网格(体素)的边长为 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绘制高度颜色点云图

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

点云梯度下采样 的相关文章

  • 【python】使用open3d进行mesh sampling

    span class token keyword import span open3d span class token keyword as span o3d mesh path span class token operator 61
  • Open3D DbScanClustering聚类算法及聚类分簇可视化及存储

    DBSCAN聚类算法 是基于密度的聚类算法 该算法需要两个参数 labels np array pcd cluster dbscan eps 0 02 min points 10 print progress True 入参 eps 定义到
  • Open3D 入门教程

    文章目录 1 概述 2 安装 3 点云读写 4 点云可视化 4 1 可视化单个点云 4 2 同一窗口可视化多个点云 4 3 可视化的属性设置 5 k d tree 与 Octree 5 1 k d tree 5 2 Octree 5 2 1
  • windows10编译open3d 0.13

    目录 写在前面 准备 获取源码 cmake cmake版本 开始cmake 1 命令行 2 cmake gui 编译 安装 测试 完 写在前面 1 环境 win10 visual studio2019 cmake3 22 0 rc1 下载源
  • 基于Open3D的点云处理12-体素化

    体素化Voxelization 体素 voxel 是像素 pixel 体积 volume 和元素 element 的组合词 相当于3D空间中的像素 体素化是通过用空间均匀大小的体素网格 voxel grid 来模拟模型或者点云的几何形态的过
  • Open3d读写ply点云文件

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 1 Open3d 安
  • open3d 点到点云之间的距离

    关键代码 dists pcd compute point cloud distance target chair pcd chair PointCloud import open3d as o3d import numpy as np if
  • 3D点云之语义分割(相关官方示例介绍)

    之前在博客中提到 会考虑用深度学习来对3D点云进行处理 接下来迈出脚步 先整几个例子来熟悉它 例子原型来源于官网 博主在其基础上做了一些代码修改 一 例子参考 1 Keras中的资源 Code examples 2 openvinotool
  • open3d 最远距离采样FPS

    关键代码 FPS pcd down pcd farthest point down sample 1000 pcd CloudPoint 如果是mesh采样 不是FPS pcl mesh sample points poisson disk
  • open3d教程(一):open3d的安装和测试(Python版本)

    1 介绍 Open3d 用于3D数据处理的现代库 Open3D 是一个开源库 支持快速开发处理 3D 数据的软件 Open3D 前端在 C 和 Python 中公开了一组精心挑选的数据结构和算法 后端经过高度优化 并设置为并行化 我们欢迎来
  • open3d,读取stl/ply/obj/off/gltf/glb三维模型,并转换成点云,保存

    1 三维模型获取 可以自己用建模软件建立一个模型 本案例使用模型的下载地址 可以从free3d免费下载 无需注册 2 导入open3d import open3d as o3d 3 open3d模型读取与可视化 模型路径 支持后缀 stl
  • Open3d之计算点云边界框

    核心函数 与Open3D中的其他几何类型一样 PointCloud几何类型具有边界框 当前 Open3D实现了AxisAlignedBoundingBox和OrientedBoundingBox 它们也可用于裁剪几何 AxisAligned
  • open3d读取、显示和保存点云数据

    1 从文件中读取点云 接口1 bool open3d io ReadPointCloud const std string filename geometry PointCloud pointcloud const ReadPointClo
  • 【点云处理技术之PCL】滤波器——直通滤波器(pcl::PassThrough)

    直通滤波器 是直接根据滤波器设定的条件 选择自己所需点云 可以选择保留设定范围内的点云 也可以选择滤除设定范围内的点云 保留或者滤出是由setFilterLimitsNegative进行模式开关的 代码中 设定z轴的条件 保留z方向范围 0
  • python open3d点云可视化(本节会根据实际所用持续更新)

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 为了便于加强对点云数
  • Open3D点云处理算法最全合集

    Open3D点云处理算法最全合集 致力于搜集可运行 可视化较好的Open3D算法 持续更新中 1 Open3D 点云读取及可视化 离群点去除 2 Open3D 点云体素格下采样 3 Open3D 点云KdTree建立 3种近邻搜索及结果可视
  • 【3D人脸】Open3D学习笔记 一

    最近头疼于点云法向量的计算 实在找不到python的相关资料 想起来Open3D这个专门的工具 一搜还真有 踩了很多坑 记录一下 Open3D官方文档 http www open3d org docs release index html
  • 八种点云聚类方法(一)— DBSCAN

    本文为博主原创文章 未经博主允许不得转载 本文为专栏 python三维点云从基础到深度学习 系列文章 地址为 https blog csdn net suiyingy article details 124017716 传统机器学习聚类的方
  • open3D点云分割

    将底面和物体分割开 import time import open3d as o3d import numpy as np mesh box o3d geometry TriangleMesh create box width 0 4 he
  • 对 pcl::StatisticalOutlierRemoval 滤波器的理解

    对 pcl StatisticalOutlierRemoval 滤波器的理解 注 以下内容基于与 GPT 4 的交流并结合个人理解整理而成 若有描述不准确或模糊之处 欢迎指正 参数配置 setMeanK int meanK 此参数设置每个点

随机推荐

  • MyBatis如何实现多表联查

    一 通过映射配置文件实现多表联查 首先 使用Mysql数据库 创建两个表 分别为学生表Student表和班级表Class表 在Student表中添加列classid参照主表的列id的外键约束 学生表Student表 班级表Class表 现在
  • (三)工作流Activiti7-个人任务查询及完成

    前言 在 上篇文章中我们已经完成了流程的定义和部署 通过repositoryService部署并启动流程后就已经开始给每个负责人分配任务了 比如下图 assignee可以写死但一般都是通过变量来设置任务负责人的 在启动一个流程实例的时候就可
  • linux命令之chmod

    chmod 英文全拼 change mode 命令是控制用户对文件的权限的命令 常见报错提醒 Permission denied 解决方法 chmod 777 lady sh 原理详解 首先 查看文件权限 命令 ls l 若想设置文件权限
  • idea整合git解决代码冲突(图文通俗易懂)

    idea整合git的步骤如下 1 快速搭建一个web工程 2 在idea对git进行相关配置 自行下载安装git 配置git exe的路径 自行注册github账号 添加github账号到idea 3 对项目新建一个本地仓库 新建仓库后 找
  • FTP连接时出现“227 Entering Passive Mode” 的解决方法

    今天从公网的服务器连接本地内网的FTP server copy文件时 系统老是提示227 Entering Passive Mode xxx xxx xxx xxx x 很是奇怪 于是上网找资料仔细研究了一下 原来FTP有两种工作模式 PO
  • Python从入门到放弃 (一) Python3的安装 以及pip安装

    编程语言只是一个工具 能够让这个工具具有什么样的效果取决于使用这个工具的人 笔者根据自己的经验写了一下Python3的学习 让新手小白快速入门 不足之处请读者指正 一 Python入门学习介绍 任何一门编程语言都需要开发环境 有了开发环境才
  • IP地址大全之IPV4版

    首先给大家分享一个巨牛巨牛的人工智能教程 是我无意中发现的 教程不仅零基础 通俗易懂 而且非常风趣幽默 还时不时有内涵段子 像看小说一样 哈哈 我正在学习中 觉得太牛了 所以分享给大家 点这里可以跳转到教程 IP 地 址 我们平时说的IP地
  • 响应式开发一招致胜 学习视频 分享

    转载于 https www cnblogs com ios9 p 8526562 html
  • VS远程调试与附加调试

    一 发送文件到目标机器 我的程序是x64 所以拷贝这个文件夹到目标机器即可 二 配置目标机器为远程调试状态 zheg 1 双击msvsmon exe 2 配置无需密码直接可以远程 工具 gt 选项 gt 选择无身份验证 允许任何用户进行调试
  • Python numpy函数:shape用法

    shape函数是numpy core fromnumeric中的函数 它的功能是读取矩阵的长度 比如shape 0 就是读取矩阵第一维度的长度 shape的输入参数可以是一个整数 表示维度 也可以是一个矩阵 以下例子可能会好理解一些 1 参
  • Python对比两个文件夹的文件差异并导出差异

    python脚本 coding utf 8 目录对比工具 包含子目录 并列出 1 A比B多了哪些文件 2 B比A多了哪些文件 3 二者相同的文件 md5比较 import os import time import difflib impo
  • vue使用axios发送post请求携带json body参数,后端使用@RequestBody进行接收

    前言 最近在做自己项目中 做一个非常简单的新增用户场景 但是使用原生axios发送post请求的时候 还是踩了不少坑的 唉 说多了都是泪 小小一个新增业务 在自己前后端一起开发的时候 硬是搞了好久 下面就把问题总结分享下 防止后人再踩坑 接
  • Python3---numpy的详细解读,小白疯狂收藏

    前言 近日梳理python3 的numpy的相关知识点 故自我整理成笔记发布 一是供大家评论和建议 查缺补漏自我知识框架 二是可以进一步熟悉知识 达到更好的融汇贯通的情况 希望看到的兄弟姐妹可以不吝赐教 感激不尽 1 维度 一维数组 二维数
  • 文件服务器fuse,分布式文件系统glusterfs安装步骤

    我的系统是 RHEL5 可能环境不一样 需要安装的第三方依赖不一样啊 反正大家在安装的过程中缺少什么就去安装什么 一般都会有提示的 下载 glusterfs 3 2 0 tar gz 源码包 随便解压到一个目录 glusterfs 需要 f
  • Dynamics CRM 2011/2013 通过Javascript给lookup字段赋值

    仅仅做下记录 因为老是用到但老是忘记 var value new Array value 0 new Object value 0 id idValue value 0 name textValue value 0 entityType t
  • hadoop:编写jpsall脚本错误bash: 行 1: jps: 未找到命令

    jpsall脚本 集群使用jps命令查看集群运行情况 bin bash for host in hadoop102 hadoop103 hadoop104 do echo host ssh host jps done 运行jpsall报错
  • caffe中forward过程总结 2

    前面http blog csdn net buyi shizi article details 51504276 总结的是caffe有和卷积有关的forward过程 下面我们总结一下卷积之后和全连接网络Inner Product Layer
  • Linux音视频编程学习(1)

    1 linux音视频概念 声音作为一种模拟信号 需要被A D转换器转换成数字信号 才能被存储在计算机中 因此A D转换视为3步 采样 量化和编码 采样 采样器每个一段时间来读取一次模拟信号 用这些离散的值来代表整个模拟信号的过程 单位时间内
  • MES系统产品规划

    某互联网科技有限公司 MES系统规划初稿 说明书V1 0 审核 批准清单 姓名 职位 签名 日期 撰写 张先生 产品总监 批准 版本修订历史 版本 版本日期 作者 公司 版本描述 A 2021 08 14 张先生 文档初稿 提交供内部修改
  • 点云梯度下采样

    点云下采样又称点云精简 均匀网格下采样 均匀网格下采样法是建立在空间包围盒精简算法之上对散乱点云快速简化的一种算法 其基本思想为 根据点云数据的密度确定最小三维网格 体素 的边长为 a b c a b c a b c 计