首页 > 其他分享 >点云数据转换为八叉树结构及其应用

点云数据转换为八叉树结构及其应用

时间:2024-12-11 10:27:58浏览次数:10  
标签:node Vector3 为八叉 min max 树结构 点云 corner 节点

一、引言

点云数据在三维空间信息表示中具有重要地位,广泛应用于计算机视觉、机器人导航、地理信息系统等众多领域。然而,原始点云数据往往具有大规模、无序性等特点,这给数据的存储、处理和分析带来了诸多挑战。八叉树结构作为一种有效的空间划分和数据组织方式,能够将三维空间递归地划分为八个子空间,从而对复杂的三维数据进行层次化表示和高效管理。本文将深入探讨如何将点云数据集合转换为八叉树结构,并详细介绍转换后的八叉树在不同场景下的应用,同时给出 C# 和 Python 的实现代码,旨在帮助读者全面理解和掌握这一关键技术及其实际应用。

二、八叉树基本原理

(一)空间划分概念

八叉树是一种基于树状数据结构的空间划分方法,它将三维空间递归地细分为八个大小相等的子立方体(八叉)。根节点表示整个三维空间,然后根据一定的规则将其划分为八个子节点,每个子节点对应一个子立方体。这个过程可以持续递归进行,直到满足特定的终止条件,例如子立方体中的点数量小于某个阈值,或者达到预定的树的深度。

例如,考虑一个简单的三维场景,包含一个长方体形状的物体。八叉树的根节点代表整个包含该长方体的空间范围。在第一次划分时,根节点被划分为八个子立方体,其中只有部分子立方体包含长方体的点云数据。对于包含点云数据的子立方体,可以进一步细分,直到达到所需的精度或满足其他停止标准。这种层次化的划分方式使得八叉树能够有效地组织和表示三维空间中的数据,减少数据存储和处理的复杂度。

(二)节点属性与数据存储

在八叉树结构中,每个节点具有一些关键属性。除了表示其对应的空间范围(如最小坐标和最大坐标界定的立方体)外,节点还可能包含指向其子节点的引用(如果有子节点),以及与该节点相关的点云数据信息。

对于叶节点(即没有子节点的节点),通常会存储属于该节点所代表空间范围内的点云数据点。这些点可以以不同的方式存储,例如直接存储点的坐标列表,或者采用一些更紧凑的数据结构来减少内存占用。而内部节点主要用于组织空间划分,通过指向子节点的引用构建起整个八叉树的层次结构。

在实际应用中,根据具体需求和场景,可以灵活设计节点的数据结构。例如,可以在节点中添加额外的信息,如该节点内点云数据的统计信息(如均值、方差等),以便在后续的处理中能够更快速地进行一些分析和决策。

三、点云数据转换为八叉树的算法流程

(一)确定空间范围

首先,需要确定点云数据所占据的三维空间范围。遍历点云数据中的所有点,找到其在x 、y、z三个维度上的最小值和最大值,从而确定一个能够包含所有点云数据的最小立方体空间。这一步骤为后续的空间划分提供了基础的边界信息。

例如,对于一个包含n个点的点云数据集合P={p1,p2,...,pn},其中pi={xi,yi,zi}。计算,同理可得 。则整个点云数据的空间范围可以表示为

(二)递归划分空间

从根节点开始,将确定的三维空间范围划分为八个子空间,对应八个子节点。对于每个子空间,判断点云数据中有哪些点落在该子空间内。如果子空间内的点数量超过设定的阈值,或者还未达到预定的树的深度,则继续对该子空间进行递归划分,重复上述过程。

在划分过程中,可以根据点的坐标与子空间边界的关系来确定点所属的子空间。例如,对于一个子空间范围,一个点,如果(其中),则点p在x维度上可能属于前四个子空间之一,再结合y和z维度的判断,可以确定其具体所属的子空间。

(三)分配点到节点

在划分空间的同时,将点云数据中的点分配到相应的叶节点中。当一个子空间不再进行划分时,该子空间对应的叶节点就存储落在其范围内的点云数据点。这样,通过不断地划分和分配,最终构建起完整的八叉树结构,将原始无序的点云数据组织成层次化的空间数据结构。

四、C# 实现

(一)数据结构定义

在 C# 中,使用 OpenTK 库中的 List<Vector3> 来存储点云数据。首先定义八叉树节点的数据结构:

using OpenTK;
using System.Collections.Generic;

class OctreeNode
{
    // 节点对应的空间范围
    public Vector3 Min { get; set; }
    public Vector3 Max { get; set; }

    // 子节点列表
    public List<OctreeNode> Children { get; set; }

    // 存储在该节点的点云数据点(叶节点)
    public List<Vector3> Points { get; set; }

    public OctreeNode(Vector3 min, Vector3 max)
    {
        Min = min;
        Max = max;
        Children = new List<OctreeNode>();
        Points = new List<Vector3>();
    }
}

(二)八叉树构建函数

接下来实现构建八叉树的函数:

class Octree
{
    private OctreeNode root;
    private int maxPointsPerNode;
    private int maxDepth;

    public Octree(List<Vector3> pointCloud, int maxPoints = 10, int depth = 10)
    {
        // 确定点云空间范围
        Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
        foreach (var point in pointCloud)
        {
            min = Vector3.ComponentMin(min, point);
            max = Vector3.ComponentMax(max, point);
        }

        // 创建根节点
        root = new OctreeNode(min, max);

        // 设置最大节点点数和最大深度
        maxPointsPerNode = maxPoints;
        maxDepth = depth;

        // 开始构建八叉树
        BuildOctree(root, pointCloud, 0);
    }

    private void BuildOctree(OctreeNode node, List<Vector3> points, int depth)
    {
        if (depth >= maxDepth || points.Count <= maxPointsPerNode)
        {
            // 将点分配到叶节点
            node.Points.AddRange(points);
            return;
        }

        // 划分空间并创建子节点
        List<List<Vector3>> subPointLists = new List<List<Vector3>>();
        for (int i = 0; i < 8; i++)
        {
            subPointLists.Add(new List<Vector3>());
        }

        Vector3 center = (node.Min + node.Max) / 2.0f;
        foreach (var point in points)
        {
            int index = GetOctantIndex(node, point, center);
            subPointLists[index].Add(point);
        }

        // 创建子节点并递归构建八叉树
        for (int i = 0; i < 8; i++)
        {
            if (subPointLists[i].Count > 0)
            {
                Vector3 subMin = GetSubMin(node.Min, node.Max, i);
                Vector3 subMax = GetSubMax(node.Min, node.Max, i);
                OctreeNode childNode = new OctreeNode(subMin, subMax);
                node.Children.Add(childNode);
                BuildOctree(childNode, subPointLists[i], depth + 1);
            }
        }
    }

    private int GetOctantIndex(OctreeNode node, Vector3 point, Vector3 center)
    {
        int index = 0;
        if (point.X >= center.X) index |= 4;
        if (point.Y >= center.Y) index |= 2;
        if (point.Z >= center.Z) index |= 1;
        return index;
    }

    private Vector3 GetSubMin(Vector3 min, Vector3 max, int octant)
    {
        Vector3 center = (min + max) / 2.0f;
        Vector3 subMin = min;
        if ((octant & 4) == 0) subMin.X = center.X;
        if ((octant & 2) == 0) subMin.Y = center.Y;
        if ((octant & 1) == 0) subMin.Z = center.Z;
        return subMin;
    }

    private Vector3 GetSubMax(Vector3 min, Vector3 max, int octant)
    {
        Vector3 center = (min + max) / 2.0f;
        Vector3 subMax = max;
        if ((octant & 4)!= 0) subMax.X = center.X;
        if ((octant & 2)!= 0) subMax.Y = center.Y;
        if ((octant & 1)!= 0) subMax.Z = center.Z;
        return subMax;
    }
}

上述代码首先确定点云数据的空间范围并创建根节点,然后通过 BuildOctree 函数递归地构建八叉树。在构建过程中,根据点的位置将其分配到相应的子空间列表中,并为非空的子空间创建子节点,继续递归构建。GetOctantIndex 函数用于确定点所属的八叉树子空间索引,GetSubMin 和 GetSubMax 函数用于计算子空间的最小和最大坐标。

(三)八叉树的应用示例:空间查询

八叉树构建完成后,可以进行各种应用操作,例如空间查询。下面是一个简单的在八叉树中查询给定空间范围内点的函数示例:

public List<Vector3> QueryPointsInRange(Vector3 queryMin, Vector3 queryMax)
{
    List<Vector3> result = new List<Vector3>();
    QueryPointsInRangeRecursive(root, queryMin, queryMax, result);
    return result;
}

private void QueryPointsInRangeRecursive(OctreeNode node, Vector3 queryMin, Vector3 queryMax, List<Vector3> result)
{
    // 检查节点空间范围与查询范围的关系
    if (IsIntersecting(node.Min, node.Max, queryMin, queryMax))
    {
        if (node.Children.Count == 0)
        {
            // 叶节点,直接添加符合条件的点
            foreach (var point in node.Points)
            {
                if (IsPointInRange(point, queryMin, queryMax))
                {
                    result.Add(point);
                }
            }
        }
        else
        {
            // 内部节点,递归查询子节点
            foreach (var child in node.Children)
            {
                QueryPointsInRangeRecursive(child, queryMin, queryMax, result);
            }
        }
    }
}

private bool IsIntersecting(Vector3 boxMin1, Vector3 boxMax1, Vector3 boxMin2, Vector3 boxMax2)
{
    return!(boxMax1.X < boxMin2.X || boxMin1.X > boxMax2.X ||
             boxMax1.Y < boxMin2.Y || boxMin1.Y > boxMax2.Y ||
             boxMax1.Z < boxMin2.Z || boxMin1.Z > boxMax2.Z);
}

private bool IsPointInRange(Vector3 point, Vector3 queryMin, Vector3 queryMax)
{
    return queryMin.X <= point.X && point.X < queryMax.X &&
           queryMin.Y <= point.Y && point.Y < queryMax.Y &&
           queryMin.Z <= point.Z && point.Z < queryMax.Z;
}

这个空间查询函数接受一个查询空间范围,通过递归遍历八叉树,检查每个节点与查询范围的相交情况,对于叶节点,进一步判断其中的点是否在查询范围内,并将符合条件的点添加到结果列表中。

五、Python 实现

(一)数据结构定义

在 Python 中,我们可以使用自定义的类来表示八叉树节点和八叉树。首先定义八叉树节点类:

class OctreeNode:
    def __init__(self, min_corner, max_corner):
        # 节点对应的空间范围
        self.min = min_corner
        self.max = max_corner
        # 子节点列表
        self.children = []
        # 存储在该节点的点云数据点(叶节点)
        self.points = []

(二)八叉树构建函数

class Octree:
    def __init__(self, pointCloud, maxPoints=10, maxDepth=10):
        # 确定点云空间范围
        x_min, y_min, z_min = float('inf'), float('inf'), float('inf')
        x_max, y_max, z_max = float('-inf'), float('-inf'), float('-inf')
        for point in pointCloud:
            x, y, z = point
            x_min = min(x_min, x)
            x_max = max(x_max, x)
            y_min = min(y_min, y)
            y_max = max(y_max, y)
            z_min = min(z_min, z)
            z_max = max(z_max, z)
        self.root = OctreeNode((x_min, y_min, z_min), (x_max, y_max, z_max))
        self.maxPointsPerNode = maxPoints
        self.maxDepth = maxDepth
        # 开始构建八叉树
        self.buildOctree(self.root, pointCloud, 0)

    def buildOctree(self, node, points, depth):
        if depth >= self.maxDepth or len(points) <= self.maxPointsPerNode:
            # 将点分配到叶节点
            node.points = points
            return
        # 划分空间并创建子节点
        subPointLists = [[] for _ in range(8)]
        center = ((node.min[0] + node.max[0]) / 2, (node.min[1] + node.max[1]) / 2, (node.min[2] + node.max[2]) / 2)
        for point in points:
            index = self.getOctantIndex(node, point, center)
            subPointLists[index].append(point)
        # 创建子节点并递归构建八叉树
        for i in range(8):
            if len(subPointLists[i]) > 0:
                subMin = self.getSubMin(node.min, node.max, i)
                subMax = self.getSubMax(node.min, node.max, i)
                childNode = OctreeNode(subMin, subMax)
                node.children.append(childNode)
                self.buildOctree(childNode, subPointLists[i], depth + 1)

    def getOctantIndex(self, node, point, center):
        index = 0
        if point[0] >= center[0]: index |= 4
        if point[1] >= center[1]: index |= 2
        if point[2] >= center[2]: index |= 1
        return index

    def getSubMin(self, min_corner, max_corner, octant):
        center = ((min_corner[0] + max_corner[0]) / 2, (min_corner[1] + max_corner[1]) / 2, (min_corner[2] + max_corner[2]) / 2)
        subMin = list(min_corner)
        if (octant & 4) == 0: subMin[0] = center[0]
        if (octant & 2) == 0: subMin[1] = center[1]
        if (octant & 1) == 0: subMin[2] = center[2]
        return tuple(subMin)

    def getSubMax(self, min_corner, max_corner, octant):
        center = ((min_corner[0] + max_corner[0]) / 2, (min_corner[1] + max_corner[1]) / 2, (min_corner[2] + max_corner[2]) / 2)
        subMax = list(max_corner)
        if (octant & 4)!= 0: subMax[0] = center[0]
        if (octant & 2)!= 0: subMax[1] = center[1]
        if (octant & 1)!= 0: subMax[2] = center[2]
        return tuple(subMax)

(三)八叉树的应用示例:空间查询

同样,在 Python 实现中,构建完八叉树后可以进行空间查询操作。以下是一个简单的空间查询函数示例:

    def queryPointsInRange(self, queryMin, queryMax):
        result = []
        self.queryPointsInRangeRecursive(self.root, queryMin, queryMax, result)
        return result

    def queryPointsInRangeRecursive(self, node, queryMin, queryMax, result):
        # 检查节点空间范围与查询范围的关系
        if self.isIntersecting(node.min, node.max, queryMin, queryMax):
            if len(node.children) == 0:
                # 叶节点,直接添加符合条件的点
                for point in node.points:
                    if self.isPointInRange(point, queryMin, queryMax):
                        result.append(point)
            else:
                # 内部节点,递归查询子节点
                for child in node.children:
                    self.queryPointsInRangeRecursive(child, queryMin, queryMax, result)

    def isIntersecting(self, boxMin1, boxMax1, boxMin2, boxMax2):
        return not (boxMax1[0] < boxMin2[0] or boxMin1[0] > boxMax2[0] or
                    boxMax1[1] < boxMin2[1] or boxMin1[1] > boxMax2[1] or
                    boxMax1[2] < boxMin2[2] or boxMin1[2] > boxMax2[2])

    def isPointInRange(self, point, queryMin, queryMax):
        return queryMin[0] <= point[0] < queryMax[0] and \
               queryMin[1] <= point[1] < queryMax[1] and \
               queryMin[2] <= point[2] < queryMax[2]

这个 queryPointsInRange 函数接受一个查询空间范围(由 queryMin 和 queryMax 界定),通过递归遍历八叉树来查找在该范围内的点。首先判断节点的空间范围与查询范围是否相交,如果相交且是叶节点,则检查其中的点是否在查询范围内并添加到结果列表;如果是内部节点,则递归地对其每个子节点进行相同的操作。

(四)八叉树的可视化

为了更好地理解八叉树对空间的划分和点云数据的组织情况,可以进行八叉树的可视化。这里可以使用一些 Python 的可视化库,如 matplotlib 结合 mayavi 库来实现简单的三维可视化。

首先,安装所需库:

pip install mayavi matplotlib

然后,编写可视化函数:

from mayavi import mlab
import matplotlib.pyplot as plt


def visualizeOctree(octree):
    # 绘制八叉树节点的立方体
    def draw_cube(min_corner, max_corner, color=(0.5, 0.5, 0.5), opacity=0.1):
        x = [min_corner[0], max_corner[0], max_corner[0], min_corner[0], min_corner[0], max_corner[0], max_corner[0], min_corner[0]]
        y = [min_corner[1], min_corner[1], max_corner[1], max_corner[1], min_corner[1], min_corner[1], max_corner[1], max_corner[1]]
        z = [min_corner[2], min_corner[2], min_corner[2], min_corner[2], max_corner[2], max_corner[2], max_corner[2], max_corner[2]]
        mlab.plot3d(x, y, z, color=color, tube_radius=None, opacity=opacity)

    # 递归绘制八叉树
    def drawOctreeRecursive(node):
        draw_cube(node.min, node.max)
        if node.children:
            for child in node.children:
                drawOctreeRecursive(child)

    # 绘制点云数据
    def drawPoints(points):
        x = [p[0] for p in points]
        y = [p[1] for p in points]
        z = [p[2] for p in points]
        mlab.points3d(x, y, z, scale_factor=0.01)

    # 绘制八叉树结构
    drawOctreeRecursive(octree.root)
    # 绘制点云数据
    drawPoints(octree.getPoints())
    mlab.show()

在上述代码中,draw_cube 函数用于绘制一个立方体表示八叉树的节点空间范围,drawOctreeRecursive 函数通过递归遍历八叉树来绘制所有节点的立方体,drawPoints 函数用于绘制原始点云数据中的点。最后调用 mlab.show 来显示可视化结果。

可以在创建八叉树后调用 visualizeOctree 函数进行可视化:

# 假设 pointCloud 是已有的点云数据列表
octree = Octree(pointCloud)
visualizeOctree(octree)

通过可视化,可以直观地看到八叉树如何将三维空间划分为不同层次的子空间,以及点云数据在这些子空间中的分布情况,有助于进一步分析和理解八叉树结构在点云数据处理中的作用和效果。这对于调试和优化八叉树相关算法,以及深入研究点云数据的空间特性都具有重要意义。

综上所述,无论是 C# 还是 Python 实现,将点云数据转换为八叉树结构都为点云数据的高效处理和分析提供了有力手段,并且可以根据具体需求进一步拓展八叉树的应用,如碰撞检测、空间索引等,在众多三维数据处理相关领域发挥重要作用。

标签:node,Vector3,为八叉,min,max,树结构,点云,corner,节点
From: https://blog.csdn.net/m0_60315436/article/details/144382229

相关文章

  • NUBIGONPro 6.2点云渲染视频制作软件
    NUBIGONPro6.2是实景三维应用领域打造的点云视觉表现与场景动画制作应用系统,着力于协助专业的行业用户实现大批量点云数据高质量视觉效果渲染,轻松为点云叠加工程/工业领域三维矢量设计数据,以及高效炫酷的点云三维场景动画制作等专业级应用。 NUBIGON功能介绍:项目上采集了......
  • 3D点云-Pointnet++模型解读(附源码+论文)
    3D点云-Pointnet++模型代码链接:pointnet2-pytorch-study(关键部分代码注释详细,参考Pointnet_Pointnet2_pytorch)论文链接:PointNet++:DeepHierarchicalFeatureLearningonPointSetsinaMetricSpace官方链接:pointnet2(源码基于TensorFlow)公开3D点云数据集:modelnet4......
  • halcon3d点云补全方法
    一,主要目标是通过点云的拼接,将多个角度的点云拼接成一个完整的的点云,方法是通过计算几个点云的重叠区域刚性变换关系,然后通过全局匹配的方式,将点云进行整体的放射变换对其,再重采样!二,要用到的两个重要算子register_object_model_3d_pair(::ObjectModel3D1,ObjectModel3......
  • vxe-table 树结构单元格选取与复制粘贴
    vxe-table树结构单元格选取与复制粘贴,通过tree-config.transform使用树形表格<template><div><vxe-tablebordershow-overflowkeep-sourceheight="600":column-config="columnConfig":tree-config=&quo......
  • 无监督模板辅助点云形状对应网络
    无监督模板辅助点云形状对应网络   无监督点云形状对应旨在建立源点云和目标点云之间的逐点对应关系。现有方法通过计算点云之间的逐点特征相似度直接获得对应关系。然而,非刚性物体具有很强的变形能力和不寻常的形状,因此直接在具有非常规形状的点云之间建立对应关系是一个长......
  • 构建目录树结构
    //示例节点结构constnodes=[{id:1,parentId:null,name:'Root'},{id:4,parentId:2,name:'Grandchild1'},{id:5,parentId:2,name:'Grandchild2'},......
  • 点云配准学习1——基于SVD 分解对应点最优变换求解
    1.刚体变换问题描述        令 和 是 空间内的两组对应的点。希望找到一个刚性的变换,在最小二乘的意义上最优地对齐两个点集,也就是说,寻找一个旋转矩阵 和一个平移向量 来满足:        式中,叫>0是每个对点的权重。 2.基于SVD刚性变换矩阵计算......
  • PCL 计算点云AABB包围盒
    目录一、概述1.1原理1.2实现步骤1.3应用场景二、代码实现2.1关键函数2.1.1计算AABB2.1.2可视化AABB2.2完整代码三、实现效果PCL点云算法汇总及实战案例汇总的目录地址链接:PCL点云算法与项目实战案例汇总(长期更新)一、概述        点云的包围盒(Boundi......
  • 三维点云使用pcl实现RANSAC平面分割
    小白每日一练!点云分割分割是将点云划分为多个部分的过程,每个部分代表不同的物体或表面。在这里,我们使用RANSAC算法来识别和分离平面。(以ModelNet40为例)完整代码放在最后面啦!!测试好了可以直接使用!!RANSAC算法RANSAC算法是一种用于从一组包含异常数据的观测数据中估计数学模......
  • PCL 点云中的数学
    函数求导方差&协方差矩阵基本概念方差(Variance)衡量的是单个随机变量的变化(比如一个人在群体中的身高),概率论中方差用来度量随机变量和其数学期望(即均值)之间的偏离程度。标准差(StandardDeviation)是方差的算术平方根,用σ表示。标准差能反映一个数据集的离散程度。协方......