当前位置: 首页 > news >正文

Open3D 学习指南 (持续学习)

本章仅为个人学习整理。

Open3D: https://www.open3d.org/
Github repo: https://github.com/isl-org/Open3D

1. 概述

Open3D 是一个开源库,旨在为 3D 数据处理提供高效且易用的工具。它由 Intel 开发和维护,支持多种 3D 数据处理任务,如点云处理、3D 重建、几何处理和可视化等。

1.1 主要功能

  • 点云处理:
    • 支持点云的读取、写入和可视化。
    • 提供点云滤波、配准、分割和特征提取等功能。
  • 3D 重建:
    • 支持从深度图像生成 3D 网格。
    • 提供多视图 3D 重建算法。
  • 几何处理:
    • 支持对三角网格、体素网格和曲面的处理。
    • 提供几何变换、简化和布尔运算等功能。
  • 可视化:
    • 提供交互式的 3D 可视化工具。
    • 支持点云、网格和体素的渲染。
  • 机器学习:
    • 提供与深度学习框架的集成,支持 3D 数据的机器学习任务。
      在这里插入图片描述

2. 安装

2.1 安装 Open3D

方法一:通过 pip 安装

可以直接使用 pip 安装 Open3D:

pip install open3d

方法二:手动安装

你也可以从 PyPI 下载对应版本的 .whl 文件,然后手动安装。例如,对于 Linux x86 系统和 Python 3.9 环境:

pip install open3d-0.18.0-cp39-cp39-manylinux_2_27_x86_64.whl

方法三:安装 CPU 版本

如果不使用 NVIDIA 的 CUDA,可以考虑安装 CPU 版本:

pip install open3d-cpu

方法四:源码安装

你也可以从源码安装 Open3D。具体步骤可以参考 Open3D 的官方文档。

2.2 第三方库管理

Open3D 使用 CMake 来管理第三方库。CMake 是一个跨平台的构建系统,它可以帮助自动化软件构建过程,包括查找和配置第三方库。Open3D 通过 CMake 的 find_packageExternalProject 模块来管理第三方库。

  • 第三方库管理步骤
  1. 查找系统库

    • Open3D 使用 find_package 命令查找系统中已经安装的库。例如,查找 Eigen 库:
      find_package(Eigen3 REQUIRED)
      include_directories(${EIGEN3_INCLUDE_DIR})
      
  2. 下载和构建外部项目

    • 对于一些没有预安装的库,Open3D 使用 ExternalProject_Add 命令从源代码下载并构建这些库。例如,下载并构建 GLFW:
      include(ExternalProject)
      ExternalProject_Add(glfwGIT_REPOSITORY https://github.com/glfw/glfw.gitGIT_TAG latestCMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/third_party_install
      )
      
  3. 使用 third_party 目录

    • Open3D 在其源代码中包含了一些第三方库的副本,这些库存放在 third_party 目录下。CMakeLists.txt 文件会配置这些库的构建和链接。例如,配置和使用 Filament 库:
      add_subdirectory(third_party/filament)
      include_directories(third_party/filament/include)
      

Open3D 通过 CMake 的 find_packageExternalProject_Add 命令来查找和管理第三方库,并使用 third_party 目录包含一些必要的库。这样可以确保在不同平台上都能顺利构建和运行 Open3D。

2.3 编译原理

  1. CMake 配置

    • Open3D 使用 CMake 作为构建系统。CMakeLists.txt 文件定义了项目的构建配置,包括源文件、依赖项、编译选项等。
    • CMake 会生成适合目标平台的构建文件(如 Makefile 或 Visual Studio 项目文件)。
  2. 依赖项管理

    • Open3D 依赖多个第三方库,如 Eigen(用于线性代数计算)、GLFW(用于窗口管理)、Pybind11(用于 Python 绑定)等。
    • CMake 会自动查找和配置这些依赖项。
  3. 编译和链接

    • CMake 生成的构建文件会调用编译器(如 GCC 或 Clang)编译源代码,并链接生成目标文件(如库或可执行文件)。
    • 编译过程中会根据配置选项启用或禁用某些功能模块。
  4. Python 绑定

    • 如果启用了 Python 绑定,Open3D 会使用 Pybind11 生成 Python 模块,使得 Open3D 可以在 Python 中使用。
    • 编译过程中会生成 _pybind 模块,并将其安装到 Python 的包目录中。

3. 点云写入、读取、可视化

open3d.io.write_point_cloud 是一个用于将点云数据写入文件的函数。
open3d.io.read_point_cloud 是一个用于从文件中读取点云数据的函数。
open3d.visualization.draw_geometries 是一个用于可视化几何对象列表的函数。

3.1 点云写入文件

open3d.io.write_point_cloud(filename: os.PathLike, pointcloud: open3d.geometry.PointCloud, format: str = 'auto', write_ascii: bool = False, compressed: bool = False, print_progress: bool = False
) -> bool

参数说明
filename (os.PathLike):文件路径。
pointcloud (open3d.geometry.PointCloud):要写入的 PointCloud 对象。
format (str, optional, default='auto'):输出文件的格式。当未指定或设置为 auto 时,格式将从文件扩展名推断。
write_ascii (bool, optional, default=False):如果为 True,则以 ASCII 格式输出,否则使用二进制格式。
compressed (bool, optional, default=False):如果为 True,则以压缩格式写入。
print_progress (bool, optional, default=False):如果为 True,在控制台中显示进度条。

3.2 读取点云文件

open3d.io.read_point_cloud(filename: os.PathLike, format: str = 'auto', remove_nan_points: bool = False, remove_infinite_points: bool = False, print_progress: bool = False
) -> open3d.geometry.PointCloud

参数说明
filename (os.PathLike):文件路径。
format (str, optional, default='auto'):输入文件的格式。当未指定或设置为 auto 时,格式将从文件扩展名推断。
remove_nan_points (bool, optional, default=False):如果为 True,则移除包含 NaN 值的点。
remove_infinite_points (bool, optional, default=False):如果为 True,则移除包含无限值的点。
print_progress (bool, optional, default=False):如果为 True,在控制台中显示进度条。

3.3 可视化点云

open3d.visualization.draw_geometries(geometry_list: list[open3d.geometry.Geometry], window_name: str = 'Open3D', width: int = 1920, height: int = 1080, left: int = 50, top: int = 50, point_show_normal: bool = False, mesh_show_wireframe: bool = False, mesh_show_back_face: bool = False, lookat: numpy.ndarray[numpy.float64[3, 1]] | None = None, up: numpy.ndarray[numpy.float64[3, 1]] | None = None, front: numpy.ndarray[numpy.float64[3, 1]] | None = None, zoom: float | None = None
) -> None

参数说明
geometry_list (list[open3d.geometry.Geometry]):要可视化的几何对象列表。
window_name (str, optional, default='Open3D'):可视化窗口的标题。
width (int, optional, default=1920):可视化窗口的宽度。
height (int, optional, default=1080):可视化窗口的高度。
left (int, optional, default=50):可视化窗口的左边距。
top (int, optional, default=50):可视化窗口的上边距。
point_show_normal (bool, optional, default=False):如果为 True,则显示点的法线。
mesh_show_wireframe (bool, optional, default=False):如果为 True,则显示网格的线框。
mesh_show_back_face (bool, optional, default=False):如果为 True,则显示网格三角形的背面。
lookat (Optional[numpy.ndarray[numpy.float64[3, 1]]], optional, default=None):相机的 lookat 向量。
up (Optional[numpy.ndarray[numpy.float64[3, 1]]], optional, default=None):相机的 up 向量。
front (Optional[numpy.ndarray[numpy.float64[3, 1]]], optional, default=None):相机的 front 向量。
zoom (Optional[float], optional, default=None):相机的缩放。

3.4 使用案例

import open3d as o3d
import numpy as np# 生成一个简单的点云(例如,一个立方体的顶点)
points = np.array([[0, 0, 0],[1, 0, 0],[1, 1, 0],[0, 1, 0],[0, 0, 1],[1, 0, 1],[1, 1, 1],[0, 1, 1],
])# 创建 PointCloud 对象
pcd = o3d.geometry.PointCloud()# 将点添加到 PointCloud 对象中
pcd.points = o3d.utility.Vector3dVector(points)# 保存点云到文件
o3d.io.write_point_cloud("generated_point_cloud.ply", pcd)# 读取点云文件
load_pcd = o3d.io.read_point_cloud("generated_point_cloud.ply")# 可视化点云
o3d.visualization.draw_geometries([load_pcd])

顶点点云

4. TriangleMesh 读取、保存

TriangleMeshIO节选代码: https://github.com/isl-org/Open3D/blob/main/cpp/open3d/io/TriangleMeshIO.cpp

static const std::unordered_map<std::string,std::function<bool(const std::string &,geometry::TriangleMesh &,const ReadTriangleMeshOptions &)>>file_extension_to_trianglemesh_read_function{{"ply", ReadTriangleMeshFromPLY},{"stl", ReadTriangleMeshUsingASSIMP},{"obj", ReadTriangleMeshUsingASSIMP},{"off", ReadTriangleMeshFromOFF},{"gltf", ReadTriangleMeshUsingASSIMP},{"glb", ReadTriangleMeshUsingASSIMP},{"fbx", ReadTriangleMeshUsingASSIMP},};static const std::unordered_map<std::string,std::function<bool(const std::string &,const geometry::TriangleMesh &,const bool,const bool,const bool,const bool,const bool,const bool)>>file_extension_to_trianglemesh_write_function{{"ply", WriteTriangleMeshToPLY},{"stl", WriteTriangleMeshToSTL},{"obj", WriteTriangleMeshToOBJ},{"off", WriteTriangleMeshToOFF},{"gltf", WriteTriangleMeshToGLTF},{"glb", WriteTriangleMeshToGLTF},};}  // unnamed namespace

在这段代码中,open3d 使用 assimp 来读取和写入多种三角网格文件格式。以下是支持的文件格式:

4.1 支持的读取文件格式

  • ply (使用 ReadTriangleMeshFromPLY 函数)
  • stl (使用 ReadTriangleMeshUsingASSIMP 函数)
  • obj (使用 ReadTriangleMeshUsingASSIMP 函数)
  • off (使用 ReadTriangleMeshFromOFF 函数)
  • gltf (使用 ReadTriangleMeshUsingASSIMP 函数)
  • glb (使用 ReadTriangleMeshUsingASSIMP 函数)
  • fbx (使用 ReadTriangleMeshUsingASSIMP 函数)

4.2 支持的写入文件格式

  • ply (使用 WriteTriangleMeshToPLY 函数)
  • stl (使用 WriteTriangleMeshToSTL 函数)
  • obj (使用 WriteTriangleMeshToOBJ 函数)
  • off (使用 WriteTriangleMeshToOFF 函数)
  • gltf (使用 WriteTriangleMeshToGLTF 函数)
  • glb (使用 WriteTriangleMeshToGLTF 函数)

这些函数通过文件扩展名与相应的读取和写入函数进行映射,从而支持多种三角网格文件格式的读写操作。

4.3 TriangleMesh 读取

open3d.io.read_triangle_mesh

open3d.io.read_triangle_mesh(
filename: os.PathLike, 
enable_post_processing: 
bool = False, 
print_progress: bool = False
) → open3d.geometry.TriangleMesh

参数说明
filename:文件路径,类型为 os.PathLike。
enable_post_processing:是否启用后处理,类型为 bool,默认值为 False。
print_progress:是否在控制台显示进度条,类型为 bool,默认值为 False。

请添加图片描述

import open3d as o3d# 定义文件路径
filename = "doll.stl"try:# 尝试读取三角网格mesh = o3d.io.read_triangle_mesh(filename, enable_post_processing=True, print_progress=True)# 检查网格是否成功读取if mesh.is_empty():print("Failed to read the mesh. The file format may not be supported.")else:print("Successfully read the mesh.")# 可视化三角网格o3d.visualization.draw_geometries([mesh])
except Exception as e:print(f"An error occurred: {e}")

doll
此时因为没有计算法线, 可视化出来的模型会涂成统一的灰色
然后我们可以compute_vertex_normals来计算出法线信息

import open3d as o3d
import numpy as np# 定义文件路径
filename = "doll.stl"# 读取三角网格
mesh = o3d.io.read_triangle_mesh(filename, enable_post_processing=True, print_progress=True)# 检查网格是否成功读取
if mesh.is_empty():print("Failed to read the mesh. The file format may not be supported.")
else:print("Successfully read the mesh.")# 计算法线mesh.compute_vertex_normals()# 设置网格的颜色为红色mesh.paint_uniform_color([1, 0, 0])  # 设置为红色# 创建一个可视化窗口vis = o3d.visualization.Visualizer()vis.create_window()# 添加网格到可视化窗口vis.add_geometry(mesh)# 更新几何体和渲染器vis.update_geometry(mesh)vis.poll_events()vis.update_renderer()# 渲染vis.run()vis.destroy_window()

red doll

import open3d as o3d
import numpy as np# 定义文件路径
filename = "doll.stl"# 读取三角网格
mesh = o3d.io.read_triangle_mesh(filename)
if mesh.is_empty():print("Failed to read the mesh. The file format may not be supported.")
else:print("Successfully read the mesh.")# 计算法线mesh.compute_vertex_normals()# 设置材质mat_box = o3d.visualization.rendering.MaterialRecord()mat_box.shader = 'defaultLitSSR'mat_box.base_color = [0.467, 0.467, 0.467, 0.2]  # 设置透明度为0.2mat_box.base_roughness = 0.0mat_box.base_reflectance = 0.0mat_box.base_clearcoat = 1.0mat_box.thickness = 1.0mat_box.transmission = 1.0mat_box.absorption_distance = 10mat_box.absorption_color = [0.5, 0.5, 0.5]# 使用draw函数渲染o3d.visualization.draw([{'name': 'box', 'geometry': mesh, 'material': mat_box}],show_skybox=False,width=800,height=600,bg_color=[0.5, 0.5, 0.5, 0.8]  # 设置背景颜色为灰色)

在这里插入图片描述

4.4 从mesh上提取点云

import open3d as o3d
import numpy as np# 定义文件路径
filename = "doll.stl"# 读取三角网格
mesh = o3d.io.read_triangle_mesh(filename)
if mesh.is_empty():print("Failed to read the mesh. The file format may not be supported.")
else:print("Successfully read the mesh.")# 计算法线mesh.compute_vertex_normals()# 从mesh提取点云point_cloud = mesh.sample_points_uniformly(number_of_points=10000)# 设置材质mat_box = o3d.visualization.rendering.MaterialRecord()mat_box.shader = 'defaultLitSSR'mat_box.base_color = [0.467, 0.467, 0.467, 0.2]  # 设置透明度为0.2mat_box.base_roughness = 0.0mat_box.base_reflectance = 0.0mat_box.base_clearcoat = 1.0mat_box.thickness = 1.0mat_box.transmission = 1.0mat_box.absorption_distance = 10mat_box.absorption_color = [0.5, 0.5, 0.5]# 使用draw函数渲染o3d.visualization.draw([{'name': 'box', 'geometry': mesh, 'material': mat_box},{'name': 'point_cloud', 'geometry': point_cloud}],show_skybox=False,width=800,height=600,bg_color=[0.5, 0.5, 0.5, 0.8]  # 设置背景颜色为灰色)

在这里插入图片描述

5. KD-Tree

5.1 KD-树 说明与算法原理

5.1.1 KD-树的简介

KD-树(K-Dimension Tree)是一种用于多维空间数据的搜索数据结构,其构建和搜索过程类似于二叉搜索树,但适用于高维场景。通过交替使用各维特征进行划分,KD-树能在 (O(\log N)) 的时间复杂度内实现最近邻搜索。此外,它还支持动态插入新节点,通过一种类似替罪羊树的方法保持一定的结构平衡,确保插入效率。
另外, 可以直接看wiki的说明:
https://en.wikipedia.org/wiki/K-d_tree

5.1.2 KD-树的构建

KD-树的构建过程如下:

  1. 选择分割维度:从根节点开始,依次选择各维度进行分割。通常选择数据点在该维度上的中位数作为分割点。
  2. 递归构建子树:将数据点分为两部分,左子树包含小于等于分割点的数据点,右子树包含大于分割点的数据点。递归地对每个子树进行上述操作,直到所有数据点都被处理完。

5.1.3 KD-树的搜索

KD-树的搜索过程如下:

  1. 递归搜索:从根节点开始,根据查询点在当前分割维度上的值,递归地搜索左子树或右子树。
  2. 回溯检查:在回溯过程中,检查当前节点是否比已找到的最近邻更接近查询点。如果是,则更新最近邻。
  3. 检查其他子树:如果查询点与当前分割平面的距离小于已找到的最近邻距离,则需要检查另一个子树。

5.1.4 KD-树的插入

KD-树的插入过程如下:

  1. 找到插入位置:从根节点开始,递归地找到适合插入新节点的位置。
  2. 插入新节点:在找到的插入位置插入新节点,并根据需要调整树的结构以保持平衡。

5.1.5 KD-树的应用

KD-树广泛应用于以下场景:

  1. 最近邻搜索:在点云处理、图像检索等领域,KD-树可以高效地找到距离查询点最近的点。
  2. 范围查询:在地理信息系统中,KD-树可以用于查找指定范围内的所有点。
  3. 聚类分析:在机器学习中,KD-树可以用于加速 K-means 聚类算法。

5.1.6 KD-树的C++实现

以下是一个简单的 KD-树的 C++ 实现示例:

#include <iostream>
#include <vector>
#include <algorithm>struct Point {std::vector<double> coords;Point(std::initializer_list<double> init) : coords(init) {}
};struct KDNode {Point point;KDNode* left;KDNode* right;KDNode(Point p) : point(p), left(nullptr), right(nullptr) {}
};class KDTree {
public:KDTree(const std::vector<Point>& points) {root = build(points, 0);}KDNode* build(const std::vector<Point>& points, int depth) {if (points.empty()) return nullptr;int k = points[0].coords.size();int axis = depth % k;std::vector<Point> sorted_points = points;std::sort(sorted_points.begin(), sorted_points.end(), [axis](const Point& a, const Point& b) {return a.coords[axis] < b.coords[axis];});int median = sorted_points.size() / 2;KDNode* node = new KDNode(sorted_points[median]);std::vector<Point> left_points(sorted_points.begin(), sorted_points.begin() + median);std::vector<Point> right_points(sorted_points.begin() + median + 1, sorted_points.end());node->left = build(left_points, depth + 1);node->right = build(right_points, depth + 1);return node;}void nearestNeighborSearch(const Point& query, Point& best, double& best_dist, KDNode* node, int depth) {if (!node) return;int k = query.coords.size();int axis = depth % k;double dist = distance(query, node->point);if (dist < best_dist) {best_dist = dist;best = node->point;}double diff = query.coords[axis] - node->point.coords[axis];KDNode* near = diff <= 0 ? node->left : node->right;KDNode* far = diff <= 0 ? node->right : node->left;nearestNeighborSearch(query, best, best_dist, near, depth + 1);if (std::abs(diff) < best_dist) {nearestNeighborSearch(query, best, best_dist, far, depth + 1);}}Point nearestNeighbor(const Point& query) {Point best = root->point;double best_dist = distance(query, best);nearestNeighborSearch(query, best, best_dist, root, 0);return best;}private:KDNode* root;double distance(const Point& a, const Point& b) {double dist = 0;for (size_t i = 0; i < a.coords.size(); ++i) {dist += (a.coords[i] - b.coords[i]) * (a.coords[i] - b.coords[i]);}return dist;}
};int main() {std::vector<Point> points = {{2.0, 3.0}, {5.0, 4.0}, {9.0, 6.0}, {4.0, 7.0}, {8.0, 1.0}, {7.0, 2.0}};KDTree tree(points);Point query = {9.0, 2.0};Point nearest = tree.nearestNeighbor(query);std::cout << "最近邻点: (" << nearest.coords[0] << ", " << nearest.coords[1] << ")\n";return 0;
}

KD-树是一种高效的多维空间数据搜索结构,适用于最近邻搜索、范围查询和聚类分析等场景。通过交替使用各维特征进行划分,KD-树能在 (O(\log N)) 的时间复杂度内实现高效搜索。

5.2 KDTreeFlann接口

Open3D 提供了 KDTreeFlann 类,用于高效的空间查询。主要的接口包括:

  • search_knn_vector_3d:最近邻搜索

    [k, idx, dist] = kdtree.search_knn_vector_3d(query_point, k)
    
    • query_point:查询点
    • k:返回最近邻的数量
    • 返回值:k 为找到的邻居数量,idx 为邻居的索引,dist 为邻居的距离
  • search_radius_vector_3d:半径搜索

    [k, idx, dist] = kdtree.search_radius_vector_3d(query_point, radius)
    
    • query_point:查询点
    • radius:搜索半径
    • 返回值:k 为找到的邻居数量,idx 为邻居的索引,dist 为邻居的距离
  • search_hybrid_vector_3d:固定距离搜索

    [k, idx, dist] = kdtree.search_hybrid_vector_3d(query_point, radius, max_nn)
    
    • query_point:查询点
    • radius:搜索半径
    • max_nn:返回的最大邻居数量
    • 返回值:k 为找到的邻居数量,idx 为邻居的索引,dist 为邻居的距离

5.3 Open3D 中 k-d 树的接口案例

以下是使用 Open3D 构建和查询 k-d 树的示例代码:

import open3d as o3d
import numpy as np# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(5000, 3))# 为点云设置颜色
colors = np.random.rand(5000, 3)  # 随机颜色
pcd.colors = o3d.utility.Vector3dVector(colors)# 构建k-d tree
kdtree = o3d.geometry.KDTreeFlann(pcd)# 查询k-d tree中的最近邻
query_point = np.random.rand(3)
[k, idx, dist] = kdtree.search_knn_vector_3d(query_point, 10)
print("查询点:", query_point)
print("k-d tree最近邻索引:", idx)
print("k-d tree最近邻距离:", dist)# 提取最近邻点
nearest_points = np.asarray(pcd.points)[idx, :]# 创建查询点和最近邻点的点云
query_pcd = o3d.geometry.PointCloud()
query_pcd.points = o3d.utility.Vector3dVector([query_point])
query_pcd.paint_uniform_color([1, 0, 0])  # 将查询点设置为红色nearest_pcd = o3d.geometry.PointCloud()
nearest_pcd.points = o3d.utility.Vector3dVector(nearest_points)
nearest_pcd.paint_uniform_color([0, 1, 0])  # 将最近邻点设置为绿色# 可视化点云、查询点和最近邻点
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(pcd)
vis.add_geometry(query_pcd)
vis.add_geometry(nearest_pcd)# 调整点云大小
opt = vis.get_render_option()
opt.point_size = 2.0  # 设置原始点云大小
opt.background_color = np.asarray([0.8, 0.8, 0.8])  # 设置背景颜色# 放大最近邻点的大小
for i in range(len(nearest_pcd.points)):sphere = o3d.geometry.TriangleMesh.create_sphere(radius=0.02)sphere.translate(nearest_pcd.points[i])sphere.paint_uniform_color([0, 1, 0])vis.add_geometry(sphere)# 更新可视化
vis.poll_events()
vis.update_renderer()
vis.run()
vis.destroy_window()

在这里插入图片描述

  • 创建点云:生成一个包含 1000 个随机点的点云。
  • 构建 k-d 树:使用 o3d.geometry.KDTreeFlann 构建 k-d 树。
  • 查询最近邻:使用 search_knn_vector_3d 方法查询给定点的 5 个最近邻。
  • 提取最近邻点:从点云中提取最近邻点。
  • 设置颜色:将原始点云设置为灰色,查询点设置为红色,最近邻点设置为绿色。
  • 可视化:将点云、查询点和最近邻点一起可视化。

6. Octree 八叉树

Octree 八叉树是一种用于描述三维空间的树状数据结构。它的基本思想是递归地将三维空间划分成更小的体积单元,每个节点表示一个正方体的体积元素,每个节点有八个子节点,将八个子节点所表示的体积元素加在一起就等于父节点的体积。
另外, 可以直接看wiki的说明:
https://en.wikipedia.org/wiki/Octree

6.1 基本原理

6.1.1 构建八叉树

  1. 根节点:八叉树的根节点表示整个三维空间或一个较大的正方体。
  2. 划分空间:将空间划分为八个相等的子空间,每个子空间对应一个子节点。
  3. 递归划分:对于每个子节点,如果其包含的元素数量超过预设阈值,则继续递归地将该子节点对应的空间再划分为八个更小的子空间,直到每个子节点包含的元素数量小于或等于阈值,或者达到设定的最大深度。

6.1.2 节点结构

每个八叉树节点包含以下信息:

  • 边界(Boundary):定义了节点所代表的空间区域。
  • 子节点(Children):指向八个子节点的指针。
  • 元素(Elements):节点所包含的元素列表,通常是点、物体或其他空间实体。

6.1.3 空间划分

在三维空间中,每个节点代表一个正方体,可以通过中心点和边长来定义。将正方体沿三个坐标轴(x、y、z)各切一刀,就可以得到八个子正方体。

6.2 应用

6.2.1 空间划分

八叉树常用于三维空间的分层表示和管理,例如在计算机图形学中用于加速光线追踪和碰撞检测。通过将复杂的三维场景划分成更小的区域,可以大大减少需要处理的元素数量,从而提高计算效率。

6.2.2 最近邻搜索

在三维空间中查找某个点的最近邻居时,可以利用八叉树快速缩小搜索范围。通过递归地检查包含目标点的节点及其相邻节点,可以高效地找到最近邻居。

6.2.3 碰撞检测

在物理引擎中,八叉树被广泛用于碰撞检测。通过将物体划分到不同的节点中,可以快速确定哪些物体可能发生碰撞,从而减少不必要的碰撞检测计算。

6.2.4 空间索引

八叉树也可以用于空间数据库中的空间索引,支持快速的空间查询操作,如范围查询和K近邻查询。

6.3 实现细节

6.3.1 插入元素

将一个元素插入八叉树时,首先找到包含该元素的节点,然后递归地检查该节点是否需要进一步划分,直到找到最适合的叶子节点,将元素插入其中。

6.3.2 查找元素

查找元素时,从根节点开始,根据元素的位置递归地进入对应的子节点,直到找到包含该元素的节点。

6.3.3 删除元素

删除元素时,首先找到包含该元素的节点,然后从节点的元素列表中删除该元素。如果删除后节点的元素数量小于阈值,则可以考虑合并该节点的子节点以减少树的深度。

6.4 优缺点

6.4.1 优点

  • 高效的空间划分:八叉树可以高效地划分三维空间,适用于处理大规模三维数据。
  • 快速查询:支持快速的空间查询操作,如最近邻搜索和碰撞检测。
  • 灵活性:可以自适应地划分空间,根据需要调整树的深度和节点容量。

6.4.2 缺点

  • 内存消耗:在处理大规模数据时,八叉树的节点数量可能非常庞大,导致较高的内存消耗。
  • 复杂性:实现和维护八叉树的数据结构相对复杂,特别是在处理动态数据时。

八叉树是一种强大的数据结构,广泛应用于三维空间的划分和管理。通过递归地将三维空间划分为更小的体积单元,八叉树可以高效地支持各种空间查询操作,如最近邻搜索和碰撞检测。然而,在实际应用中,需要权衡其内存消耗和实现复杂性,以确保其高效性和实用性。

6.4 Open3D Octree

Octree 类是 Open3D 中用于三维空间分割的主要类。它提供了构建、插入、查询等功能。

6.4.1 构建 Octree

要构建一个 Octree,可以使用 Octree 类并指定最大深度:

import open3d as o3d# 创建一个 Octree,指定最大深度
max_depth = 4
octree = o3d.geometry.Octree(max_depth)

6.4.2 从点云构建 Octree

可以从一个点云构建 Octree:

# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(5000, 3))# 从点云构建 Octree
octree.convert_from_point_cloud(pcd, size_expand=0.01)

6.4.3 插入点

可以向 Octree 中插入单个点:

point = [0.5, 0.5, 0.5]
octree.insert_point(point)

6.4.4 查询点

可以查询一个点是否在 Octree 中,并获取其所在的叶节点信息:

query_point = [0.5, 0.5, 0.5]
success, node_info = octree.locate_leaf_node(query_point)
print("查询成功:", success)
print("节点信息:", node_info)

6.4.5 可视化 Octree

可以使用 Open3D 的可视化工具来显示 Octree:

o3d.visualization.draw_geometries([octree])

6.4.6 完整示例代码

以下是一个完整的示例代码,展示了如何构建、插入、查询和可视化 Octree:

import open3d as o3d
import numpy as np# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(5000, 3))# 构建 Octree
max_depth = 4
octree = o3d.geometry.Octree(max_depth)
octree.convert_from_point_cloud(pcd, size_expand=0.01)# 插入多个点
points = np.random.rand(100, 3)  # 生成 100 个随机点def leaf_node_init():return o3d.geometry.OctreeColorLeafNode()def leaf_node_update(node):passdef internal_node_init():return o3d.geometry.OctreeInternalNode()def internal_node_update(node):passfor point in points:octree.insert_point(point, leaf_node_init, leaf_node_update, internal_node_init, internal_node_update)# 查询 Octree 中的点
query_point = np.array([0.5, 0.5, 0.5])
success, node_info = octree.locate_leaf_node(query_point)
print("查询点:", query_point)
print("查询成功:", success)
print("节点信息:", node_info)# 可视化 Octree 节点
def create_pointcloud_from_octree(octree):points = []colors = []def traverse(node, node_info):if isinstance(node, o3d.geometry.OctreeColorLeafNode):origin = node_info.originsize = node_info.sizedepth = node_info.depth# 根据深度设置颜色if depth == 0:color = [1, 0, 0]  # 红色elif depth == 1:color = [0, 1, 0]  # 绿色elif depth == 2:color = [0, 0, 1]  # 蓝色elif depth == 3:color = [1, 1, 0]  # 黄色else:color = [0, 1, 1]  # 青色points.append(origin + size / 2)colors.append(color)return Falseoctree.traverse(traverse)pointcloud = o3d.geometry.PointCloud()pointcloud.points = o3d.utility.Vector3dVector(np.array(points))pointcloud.colors = o3d.utility.Vector3dVector(np.array(colors))return pointcloudoctree_pointcloud = create_pointcloud_from_octree(octree)# 创建 Octree 的线框表示
def create_lineset_from_octree(octree):lines = []colors = []points = []def traverse(node, node_info):if isinstance(node, o3d.geometry.OctreeColorLeafNode) or isinstance(node, o3d.geometry.OctreeInternalNode):origin = node_info.originsize = node_info.size# 添加立方体的 12 条边cube_lines = [[0, 1], [1, 3], [3, 2], [2, 0],  # 底面[4, 5], [5, 7], [7, 6], [6, 4],  # 顶面[0, 4], [1, 5], [2, 6], [3, 7]   # 侧面]cube_points = [origin,origin + [size, 0, 0],origin + [0, size, 0],origin + [size, size, 0],origin + [0, 0, size],origin + [size, 0, size],origin + [0, size, size],origin + [size, size, size]]base_index = len(points)points.extend(cube_points)lines.extend([[base_index + start, base_index + end] for start, end in cube_lines])colors.extend([[0, 0, 0] for _ in range(len(cube_lines))])  # 黑色return Falseoctree.traverse(traverse)lineset = o3d.geometry.LineSet()lineset.points = o3d.utility.Vector3dVector(np.array(points))lineset.lines = o3d.utility.Vector2iVector(np.array(lines))lineset.colors = o3d.utility.Vector3dVector(np.array(colors))return linesetoctree_lineset = create_lineset_from_octree(octree)# 创建找到的立方体
if success:origin = node_info.originsize = node_info.sizecube = o3d.geometry.TriangleMesh.create_box(width=size, height=size, depth=size)cube.translate(origin)cube.paint_uniform_color([1, 0, 0])  # 红色# 使用默认的绘制函数来显示点云和 Octree
geometries = [octree_pointcloud, octree_lineset]
if success:geometries.append(cube)
o3d.visualization.draw_geometries(geometries)

在这里插入图片描述
这段代码展示了如何使用 Open3D 库创建一个 Octree,插入点,查询节点,并可视化 Octree 结构及其节点。

  1. 创建随机点云

    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(np.random.rand(5000, 3))
    
  2. 构建 Octree

    octree = o3d.geometry.Octree(max_depth=4)
    octree.convert_from_point_cloud(pcd, size_expand=0.01)
    
  3. 插入多个点

    points = np.random.rand(100, 3)
    for point in points:octree.insert_point(point, leaf_node_init, leaf_node_update, internal_node_init, internal_node_update)
    
  4. 查询 Octree 中的点

    query_point = np.array([0.5, 0.5, 0.5])
    success, node_info = octree.locate_leaf_node(query_point)
    
  5. 可视化 Octree 和查询结果

    octree_pointcloud = create_pointcloud_from_octree(octree)
    octree_lineset = create_lineset_from_octree(octree)
    if success:cube = o3d.geometry.TriangleMesh.create_box(width=node_info.size, height=node_info.size, depth=node_info.size)cube.translate(node_info.origin)cube.paint_uniform_color([1, 0, 0])
    geometries = [octree_pointcloud, octree_lineset, cube] if success else [octree_pointcloud, octree_lineset]
    o3d.visualization.draw_geometries(geometries)
    

6.4.7 点云分割

在 Open3D 中使用 Octree 进行点云分割可以通过以下步骤实现:

  • 创建点云并构建 Octree:

    • 创建一个点云对象并填充点数据。
    • 使用点云数据构建 Octree。
  • 遍历 Octree 并分割点云:

    • 遍历 Octree 的叶节点。
    • 根据叶节点的信息将点云分割成不同的部分。
import open3d as o3d
import numpy as np# 创建多个点云簇
def create_clustered_point_cloud(num_clusters=5, points_per_cluster=2000, cluster_radius=0.05):points = []for i in range(num_clusters):cluster_center = np.random.rand(3)cluster_points = cluster_center + cluster_radius * np.random.randn(points_per_cluster, 3)points.append(cluster_points)return np.vstack(points)# 生成点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(create_clustered_point_cloud())# 构建 Octree
max_depth = 4  # 调整 Octree 的深度
octree = o3d.geometry.Octree(max_depth)
octree.convert_from_point_cloud(pcd, size_expand=0.01)# 分割点云并为每个部分赋予不同的颜色
def segment_point_cloud(octree):segments = []colors = np.random.rand(100, 3)  # 生成随机颜色color_index = 0def traverse(node, node_info):nonlocal color_indexif isinstance(node, o3d.geometry.OctreeLeafNode):# 获取叶节点中的点segment = pcd.select_by_index(node.indices)segment.paint_uniform_color(colors[color_index % len(colors)])segments.append(segment)color_index += 1return Falseoctree.traverse(traverse)return segments# 获取分割后的点云部分
segments = segment_point_cloud(octree)# 检查是否有分割后的点云部分
if len(segments) == 0:print("没有分割后的点云部分,请检查 Octree 构建和遍历逻辑。")
else:# 可视化分割后的点云o3d.visualization.draw_geometries(segments)

点云分割

6.4.8 点云过滤
在 Open3D 中使用 Octree 进行点云滤波可以通过以下步骤实现:

  • 创建点云并构建 Octree。
  • 定义滤波条件。
  • 遍历 Octree 并应用滤波条件。
  • 生成滤波后的点云。
import open3d as o3d
import numpy as np# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(10000, 3))# 构建 Octree
max_depth = 4  # 调整 Octree 的深度
octree = o3d.geometry.Octree(max_depth)
octree.convert_from_point_cloud(pcd, size_expand=0.01)# 定义体素滤波函数
def voxel_filter(octree, voxel_size):filtered_points = []def traverse(node, node_info):if isinstance(node, o3d.geometry.OctreeLeafNode):# 计算叶节点的中心点voxel_center = node_info.origin + node_info.size / 2filtered_points.append(voxel_center)return Falseoctree.traverse(traverse)return filtered_points# 设置体素大小
voxel_size = 0.05# 获取滤波后的点云
filtered_points = voxel_filter(octree, voxel_size)
filtered_pcd = o3d.geometry.PointCloud()
filtered_pcd.points = o3d.utility.Vector3dVector(filtered_points)# 可视化原始点云和滤波后的点云
print("原始点云点数:", len(pcd.points))
print("滤波后点云点数:", len(filtered_pcd.points))
o3d.visualization.draw_geometries([pcd], window_name="原始点云")
o3d.visualization.draw_geometries([filtered_pcd], window_name="滤波后点云")
过滤前过滤后
滤波前的点云滤波后的点云

7. 点云过滤

Open3D 提供了以下几种常用的点云滤波方法:

  1. 统计滤波 (Statistical Outlier Removal)

    • 方法:remove_statistical_outlier
    • 参数:
      • nb_neighbors:用于计算平均距离的邻居点数。
      • std_ratio:距离的标准差乘数。
  2. 半径滤波 (Radius Outlier Removal)

    • 方法:remove_radius_outlier
    • 参数:
      • nb_points:在指定半径内的最小点数。
      • radius:搜索半径。
  3. 体素下采样 (Voxel Downsampling)

    • 方法:voxel_down_sample
    • 参数:
      • voxel_size:体素的大小。
  4. Uniform Downsampling

    • 方法:uniform_down_sample
    • 参数:
      • every_k_points:每隔多少个点采样一个点。

以下是这些方法的示例代码:

import open3d as o3d
import numpy as np# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(10000, 3))# 统计滤波
pcd_statistical, ind_statistical = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=1.0)
filtered_pcd_statistical = pcd.select_by_index(ind_statistical)# 半径滤波
pcd_radius, ind_radius = pcd.remove_radius_outlier(nb_points=10, radius=0.1)
filtered_pcd_radius = pcd.select_by_index(ind_radius)# 体素下采样
voxel_size = 0.05
downsampled_pcd_voxel = pcd.voxel_down_sample(voxel_size)# Uniform 下采样
every_k_points = 10
downsampled_pcd_uniform = pcd.uniform_down_sample(every_k_points)# 可视化原始点云和过滤后的点云
print("原始点云点数:", len(pcd.points))
print("统计滤波后的点云点数:", len(filtered_pcd_statistical.points))
print("半径滤波后的点云点数:", len(filtered_pcd_radius.points))
print("体素下采样后的点云点数:", len(downsampled_pcd_voxel.points))
print("Uniform下采样后的点云点数:", len(downsampled_pcd_uniform.points))o3d.visualization.draw_geometries([pcd], window_name="原始点云")
o3d.visualization.draw_geometries([filtered_pcd_statistical], window_name="统计滤波后的点云")
o3d.visualization.draw_geometries([filtered_pcd_radius], window_name="半径滤波后的点云")
o3d.visualization.draw_geometries([downsampled_pcd_voxel], window_name="体素下采样后的点云")
o3d.visualization.draw_geometries([downsampled_pcd_uniform], window_name="Uniform下采样后的点云")

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 代码说明
  1. 统计滤波:使用 remove_statistical_outlier 方法去除离群点。该方法通过计算每个点的邻居点的平均距离,并将距离超过标准差乘数的点视为离群点。参数 nb_neighbors 指定用于计算平均距离的邻居点数,std_ratio 指定距离的标准差乘数。
  2. 半径滤波:使用 remove_radius_outlier 方法去除孤立点。该方法通过检查每个点在指定半径内的邻居点数,并将邻居点数少于指定值的点视为孤立点。参数 nb_points 指定在指定半径内的最小点数,radius 指定搜索半径。
  3. 体素下采样:使用 voxel_down_sample 方法通过体素网格下采样点云。该方法将点云划分为体素网格,并用每个体素内的点的重心来代表该体素。参数 voxel_size 指定体素的大小。
  4. Uniform 下采样:使用 uniform_down_sample 方法均匀下采样点云。该方法通过按固定间隔选择点来下采样点云。参数 every_k_points 指定每隔多少个点采样一个点。

这些方法可以帮助你在不同的场景下对点云进行预处理和优化,以提高点云处理的效率和效果。

8. 点云转换

8.1 transform:应用变换矩阵到点云

transform 方法用于将一个 4x4 的变换矩阵应用到点云上。该矩阵可以包含平移、旋转和缩放。

import open3d as o3d
import numpy as np# 创建一个随机点云
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(np.random.rand(1000, 3))# 定义一个变换矩阵
transformation_matrix = np.array([[1, 0, 0, 1],[0, 1, 0, 2],[0, 0, 1, 3],[0, 0, 0, 1]])# 应用变换矩阵到点云
pcd.transform(transformation_matrix)# 可视化变换后的点云
o3d.visualization.draw_geometries([pcd], window_name="Transformed Point Cloud")

8.2 translate:平移点云

translate 方法用于将点云沿指定的方向平移。

# 平移向量
translation_vector = np.array([1, 2, 3])# 平移点云
pcd.translate(translation_vector)# 可视化平移后的点云
o3d.visualization.draw_geometries([pcd], window_name="Translated Point Cloud")

8.3 rotate:旋转点云

rotate 方法用于将点云绕指定的轴旋转。旋转矩阵可以通过欧拉角或四元数生成。

# 定义一个旋转矩阵(绕 Z 轴旋转 45 度)
rotation_matrix = pcd.get_rotation_matrix_from_xyz((0, 0, np.pi / 4))# 旋转点云
pcd.rotate(rotation_matrix, center=(0, 0, 0))# 可视化旋转后的点云
o3d.visualization.draw_geometries([pcd], window_name="Rotated Point Cloud")

8.4 scale:缩放点云

scale 方法用于将点云按指定的比例缩放。

# 缩放比例
scale_factor = 2.0# 缩放点云
pcd.scale(scale_factor, center=pcd.get_center())# 可视化缩放后的点云
o3d.visualization.draw_geometries([pcd], window_name="Scaled Point Cloud")

9. 点云法线估计

9.1 estimate_normals:估计点云法线

estimate_normals 方法用于估计点云的法线。该方法通过计算每个点的邻域点的协方差矩阵,并求解其特征向量来确定法线方向。

  • 参数说明
  • search_param:搜索参数,定义了用于法线估计的邻域搜索方法和半径。
    • search_param=o3d.geometry.KDTreeSearchParamKNN(knn):使用 K 近邻搜索,knn 为邻居点的数量。
    • search_param=o3d.geometry.KDTreeSearchParamRadius(radius):使用半径搜索,radius 为搜索半径。

9.2 orient_normals_consistent_tangent_plane:使法线方向一致

orient_normals_consistent_tangent_plane 方法用于使点云的法线方向一致。该方法通过构建一致的切平面来调整法线方向。

  • 参数说明
  • k:用于一致性调整的邻居点数量。

9.3 详细案例

以下是一个完整的案例,展示了如何读取点云、估计法线并使法线方向一致:

import open3d as o3d
import numpy as np# 生成点云数据
def generate_point_cloud():# 生成一个简单的平面点云mesh = o3d.geometry.TriangleMesh.create_sphere(radius=1.0)pcd = mesh.sample_points_poisson_disk(number_of_points=500)return pcd# 生成点云
pcd = generate_point_cloud()# 打印点云信息
print("Point cloud before normal estimation:")
print(pcd)# 估计法线
print("Estimating normals...")
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamKNN(knn=30))# 打印估计法线后的点云信息
print("Point cloud after normal estimation:")
print(pcd)# 可视化带法线的点云
o3d.visualization.draw_geometries([pcd], point_show_normal=True, window_name="Point Cloud with Normals")# 使法线方向一致
print("Orienting normals consistently...")
pcd.orient_normals_consistent_tangent_plane(k=30)# 打印调整法线方向后的点云信息
print("Point cloud after orienting normals:")
print(pcd)# 可视化带一致法线的点云
o3d.visualization.draw_geometries([pcd], point_show_normal=True, window_name="Point Cloud with Oriented Normals")
法向生成法向统一
在这里插入图片描述在这里插入图片描述

通过 estimate_normalsorient_normals_consistent_tangent_plane 方法,你可以估计点云的法线并使其方向一致。这对于后续的点云处理和分析(如表面重建、配准等)非常重要。

10. 点云配准

Open3D 提供了多种点云配准方法,主要包括以下几种:

  1. ICP (Iterative Closest Point) 配准:这是最常用的点云配准方法之一,通过迭代地最小化两组点云之间的距离来实现配准。

  2. Colored ICP 配准:这是对传统 ICP 的改进,除了几何距离外,还考虑了颜色信息来进行配准。

  3. Global Registration (全局配准):用于初始配准,通常在没有初始对齐的情况下使用。包括 RANSAC-based 和 Fast Global Registration (FGR) 方法。

  4. Multiway Registration (多路配准):用于将多个点云配准到一个共同的参考框架中。

下面是每种方法的说明和案例:

10.1 ICP 配准

说明:通过迭代地最小化两组点云之间的距离来实现配准。

案例

import open3d as o3d
import numpy as npdef create_colored_point_cloud(color):"""创建并上色的球体点云"""pcd = o3d.geometry.TriangleMesh.create_sphere(radius=1.0).sample_points_uniformly(number_of_points=1000)pcd.paint_uniform_color(color)return pcddef visualize_point_clouds(pcd1, pcd2, window_name):"""可视化点云"""o3d.visualization.draw_geometries([pcd1, pcd2], window_name=window_name)# 生成并上色两个相似的点云
pcd1 = create_colored_point_cloud([1, 0, 0])  # 红色
pcd2 = create_colored_point_cloud([0, 1, 0])  # 绿色# 对第二个点云进行初始变换
initial_transformation = np.array([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4],[0.0, 0.0, 0.0, 1.0]])
pcd2.transform(initial_transformation)# 可视化初始点云
visualize_point_clouds(pcd1, pcd2, "Initial Point Clouds")# 使用 ICP 进行配准
threshold = 0.7  # 增加阈值
trans_init = np.eye(4)
reg_p2p = o3d.pipelines.registration.registration_icp(pcd1, pcd2, threshold, trans_init,o3d.pipelines.registration.TransformationEstimationPointToPoint()
)# 打印配准信息
print("ICP converged:", reg_p2p.inlier_rmse < threshold)
print("Fitness:", reg_p2p.fitness)
print("Inlier RMSE:", reg_p2p.inlier_rmse)
print("Transformation matrix:")
print(reg_p2p.transformation)# 计算逆矩阵
inverse_transformation = np.linalg.inv(reg_p2p.transformation)# 应用逆矩阵到第二个点云
pcd2.transform(inverse_transformation)# 可视化配准后的点云
visualize_point_clouds(pcd1, pcd2, "Aligned Point Clouds")
原始点云配准点云
init pcdaligned pcd

10.2 Colored ICP 配准

说明:在传统 ICP 的基础上,考虑了颜色信息来进行配准。

import open3d as o3d
import numpy as npdef create_colored_sphere(radius, color, density=1000):mesh = o3d.geometry.TriangleMesh.create_sphere(radius=radius)pcd = mesh.sample_points_poisson_disk(number_of_points=density)pcd.paint_uniform_color(color)return pcddef estimate_normals(pcd, radius=0.1, max_nn=30):pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=radius, max_nn=max_nn))def execute_colored_icp(source, target, max_correspondence_distance, initial_transformation):criteria = o3d.pipelines.registration.ICPConvergenceCriteria(relative_fitness=1e-3,relative_rmse=1e-3,max_iteration=500)result = o3d.pipelines.registration.registration_colored_icp(source, target, max_correspondence_distance, initial_transformation,o3d.pipelines.registration.TransformationEstimationForColoredICP(),criteria)return result# 创建两个绿色的球体
sphere1 = create_colored_sphere(1.0, [0, 1, 0], density=1000)
sphere2 = create_colored_sphere(1.0, [0, 1, 0], density=1000)# 对第二个球体进行变换
transformation = np.array([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4],[0.0, 0.0, 0.0, 1.0]])
sphere2.transform(transformation)# 估算法线
estimate_normals(sphere1)
estimate_normals(sphere2)# 可视化配准前的点云
print("配准前的点云:")
o3d.visualization.draw_geometries([sphere1, sphere2], window_name="Before Registration")# 使用彩色ICP进行配准
max_correspondence_distance = 5.0  # 增大最大对应点距离
initial_transformation = np.identity(4)
result_colored_icp = execute_colored_icp(sphere1, sphere2, max_correspondence_distance, initial_transformation)# 应用变换到第一个球体点云
sphere1.transform(result_colored_icp.transformation)# 可视化配准后的点云
print("配准后的点云:")
o3d.visualization.draw_geometries([sphere1, sphere2], window_name="After Registration")
  • 代码说明
  1. 创建彩色球体:使用 create_colored_sphere 函数创建两个绿色的球体。
  2. 对第二个球体进行变换:对第二个球体进行随机变换。
  3. 估算法线:计算球体点云的法线。
  4. 可视化配准前的点云:在配准前显示两个球体点云。
  5. 使用彩色ICP进行配准:使用彩色ICP算法进行配准。
  6. 应用变换到第一个球体点云:将配准结果应用到第一个球体点云。
  7. 可视化配准后的点云:在配准后显示两个球体点云。
原始点云配准点云
init pcdaligned pcd

10.3 全局配准

说明:用于初始配准,通常在没有初始对齐的情况下使用。

好的,以下是包含配准前后可视化的完整代码:

import open3d as o3d
import numpy as np# 生成点云数据
def generate_point_cloud():# 创建一个球体点云mesh = o3d.geometry.TriangleMesh.create_sphere(radius=1.0)pcd = mesh.sample_points_poisson_disk(number_of_points=500)return pcd# 生成源点云和目标点云
source = generate_point_cloud()
target = generate_point_cloud()# 对目标点云进行随机变换
transformation = np.array([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4],[0.0, 0.0, 0.0, 1.0]])
target.transform(transformation)# 下采样点云
voxel_size = 0.05
source_down = source.voxel_down_sample(voxel_size)
target_down = target.voxel_down_sample(voxel_size)# 估计法线
source_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))
target_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))# 计算FPFH特征
source_fpfh = o3d.pipelines.registration.compute_fpfh_feature(source_down,o3d.geometry.KDTreeSearchParamHybrid(radius=0.25, max_nn=100))
target_fpfh = o3d.pipelines.registration.compute_fpfh_feature(target_down,o3d.geometry.KDTreeSearchParamHybrid(radius=0.25, max_nn=100))# 使用RANSAC进行全局配准
result_ransac = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(source_down, target_down, source_fpfh, target_fpfh,mutual_filter=True,max_correspondence_distance=0.15,estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(False),ransac_n=4,checkers=[o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(0.15)],criteria=o3d.pipelines.registration.RANSACConvergenceCriteria(4000000, 500))print(result_ransac)# 可视化配准前的点云
source_temp = source_down.transform(np.identity(4))  # 恢复原始位置
o3d.visualization.draw_geometries([source_temp, target_down], window_name="配准前")# 可视化配准后的点云
source_temp = source_down.transform(result_ransac.transformation)
o3d.visualization.draw_geometries([source_temp, target_down], window_name="配准后")
  • 代码说明
  1. 生成点云数据:创建一个球体点云,并对目标点云进行随机变换。
  2. 下采样点云:对点云进行体素下采样,以减少计算量。
  3. 估计法线:计算点云的法线。
  4. 计算FPFH特征:计算快速点特征直方图(FPFH)特征。
  5. 使用RANSAC进行全局配准:使用 RANSAC 算法基于特征匹配进行全局配准。
  6. 可视化配准前的点云:在配准前显示源点云和目标点云。
  7. 可视化配准后的点云:在配准后显示源点云和目标点云。
初始点云配准点云
init pcdaligned pcd

10.4 多路配准

说明:用于将多个点云配准到一个共同的参考框架中。

import open3d as o3d
import numpy as npdef create_colored_sphere(radius, color, density=1000):mesh = o3d.geometry.TriangleMesh.create_sphere(radius=radius)pcd = mesh.sample_points_poisson_disk(number_of_points=density)pcd.paint_uniform_color(color)return pcddef preprocess_point_cloud(pcd, voxel_size):pcd_down = pcd.voxel_down_sample(voxel_size)radius_normal = voxel_size * 2pcd_down.estimate_normals(o3d.geometry.KDTreeSearchParamHybrid(radius=radius_normal, max_nn=30))radius_feature = voxel_size * 5pcd_fpfh = o3d.pipelines.registration.compute_fpfh_feature(pcd_down,o3d.geometry.KDTreeSearchParamHybrid(radius=radius_feature, max_nn=100))return pcd_down, pcd_fpfhdef pairwise_registration(source, target, voxel_size):source_down, source_fpfh = preprocess_point_cloud(source, voxel_size)target_down, target_fpfh = preprocess_point_cloud(target, voxel_size)distance_threshold = voxel_size * 1.5result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(source_down, target_down, source_fpfh, target_fpfh, True,distance_threshold,o3d.pipelines.registration.TransformationEstimationPointToPoint(False),4, [o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(distance_threshold)], o3d.pipelines.registration.RANSACConvergenceCriteria(4000000, 500))return resultdef full_registration(pcds, voxel_size):pose_graph = o3d.pipelines.registration.PoseGraph()odometry = np.identity(4)pose_graph.nodes.append(o3d.pipelines.registration.PoseGraphNode(odometry))for source_id in range(len(pcds)):for target_id in range(source_id + 1, len(pcds)):result = pairwise_registration(pcds[source_id], pcds[target_id], voxel_size)trans = result.transformationinformation = o3d.pipelines.registration.get_information_matrix_from_point_clouds(pcds[source_id], pcds[target_id], voxel_size * 1.5, result.transformation)if target_id == source_id + 1:odometry = np.dot(trans, odometry)pose_graph.nodes.append(o3d.pipelines.registration.PoseGraphNode(np.linalg.inv(odometry)))pose_graph.edges.append(o3d.pipelines.registration.PoseGraphEdge(source_id, target_id, trans, information, uncertain=False))else:pose_graph.edges.append(o3d.pipelines.registration.PoseGraphEdge(source_id, target_id, trans, information, uncertain=True))return pose_graphdef run_global_optimization(pose_graph):option = o3d.pipelines.registration.GlobalOptimizationOption(max_correspondence_distance=0.02,edge_prune_threshold=0.25,reference_node=0)o3d.pipelines.registration.global_optimization(pose_graph,o3d.pipelines.registration.GlobalOptimizationLevenbergMarquardt(),o3d.pipelines.registration.GlobalOptimizationConvergenceCriteria(),option)def merge_point_clouds(pcds, pose_graph):pcd_combined = o3d.geometry.PointCloud()for point_id in range(len(pcds)):pcd_transformed = pcds[point_id].transform(pose_graph.nodes[point_id].pose)pcd_combined += pcd_transformedreturn pcd_combined# 创建不同颜色和大小的球体
sphere1 = create_colored_sphere(1.0, [1, 0, 0], density=1000)  # 红色球体
sphere2 = create_colored_sphere(0.8, [0, 1, 0], density=1000)  # 绿色球体
sphere3 = create_colored_sphere(0.6, [0, 0, 1], density=1000)  # 蓝色球体# 对球体进行随机变换
transformation1 = np.array([[0.862, 0.011, -0.507, 0.5],[-0.139, 0.967, -0.215, 0.7],[0.487, 0.255, 0.835, -1.4],[0.0, 0.0, 0.0, 1.0]])
sphere2.transform(transformation1)transformation2 = np.array([[0.707, -0.707, 0.0, 1.0],[0.707, 0.707, 0.0, 0.5],[0.0, 0.0, 1.0, -0.5],[0.0, 0.0, 0.0, 1.0]])
sphere3.transform(transformation2)pcds = [sphere1, sphere2, sphere3]# 设置体素大小
voxel_size = 0.05# 可视化配准前的点云
print("配准前的点云")
o3d.visualization.draw_geometries(pcds, window_name="Before Registration")# 进行多路配准
pose_graph = full_registration(pcds, voxel_size)# 运行全局优化
run_global_optimization(pose_graph)# 合并点云
pcd_combined = merge_point_clouds(pcds, pose_graph)# 可视化配准后的点云
print("配准后的点云")
o3d.visualization.draw_geometries([pcd_combined], window_name="After Registration")
  • 代码说明
  1. 创建彩色球体:使用 create_colored_sphere 函数创建不同颜色和大小的球体。
  2. 对球体进行变换:对第二个和第三个球体进行随机变换。
  3. 预处理点云:使用 preprocess_point_cloud 函数对点云进行下采样和特征提取。
  4. 配对配准:使用 pairwise_registration 函数对两个点云进行配对配准。
  5. 全局配准:使用 full_registration 函数对所有点云进行全局配准,构建位姿图。
  6. 全局优化:使用 run_global_optimization 函数对位姿图进行全局优化。
  7. 合并点云:使用 merge_point_clouds 函数将所有点云合并到一个全局坐标系中。
  8. 可视化配准前的点云:使用 Open3D 的可视化工具显示配准前的点云。
  9. 可视化配准后的点云:使用 Open3D 的可视化工具显示配准后的点云。
原始点云配准点云
init pcdaligned pcd

这些案例展示了 Open3D 中不同点云配准方法的基本用法。

11. 点云表面重建

11.1 Alpha形状重建

Alpha形状重建是一种用于从点云数据生成三角网格的方法。它基于计算几何中的Alpha形状理论。Alpha形状是由Edelsbrunner等人在1983年提出的,它是Delaunay三角剖分的一个子集,用于描述点集的形状。

11.1.1 原理

  1. Delaunay三角剖分

    • 首先,对点云进行Delaunay三角剖分。Delaunay三角剖分是一种将点集划分为一系列三角形的算法,具有最大化最小角的性质,避免了瘦长三角形。
  2. Alpha球

    • 对于给定的参数α,定义一个半径为α的球(称为Alpha球)。Alpha球用于筛选Delaunay三角剖分中的三角形。
  3. 筛选三角形

    • 对于每个Delaunay三角剖分中的三角形,检查其外接圆的半径。如果外接圆的半径小于或等于α,则保留该三角形;否则,丢弃该三角形。
  4. 生成Alpha形状

    • 保留的三角形构成了Alpha形状。通过调整α的值,可以控制生成的形状的细节程度。较小的α值会生成更细致的形状,而较大的α值会生成更平滑的形状。

11.1.2 代码示例

以下是使用Open3D库进行Alpha形状重建的代码示例:

import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 估计法线
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))# Alpha形状重建
alpha = 0.03  # 调整alpha值
mesh_alpha = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(pcd, alpha)# 可视化Alpha形状重建结果
mesh_alpha.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_alpha], window_name=f"Alpha Shape Reconstruction with alpha={alpha}")

11.1.3 调整Alpha值

通过调整alpha值,可以生成不同细节程度的形状:

  • 较小的alpha值:生成的形状更细致,保留更多的细节。
  • 较大的alpha值:生成的形状更平滑,去除了更多的细节。

11.1.4 总结

Alpha形状重建是一种有效的从点云数据生成三角网格的方法,通过调整alpha值,可以控制生成形状的细节程度。它在计算几何和计算机图形学中有广泛的应用。

import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 估计法线
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))# 调整Alpha形状重建参数
print("调整Alpha形状重建参数...")
alphas = [0.01, 0.03, 0.05]
for alpha in alphas:mesh_alpha = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(pcd, alpha)mesh_alpha.compute_vertex_normals()title = f"Alpha Shape Reconstruction with alpha={alpha}"print(title)o3d.visualization.draw_geometries([mesh_alpha], window_name=title)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

11.2 泊松重建

泊松重建(Poisson Surface Reconstruction)是一种从点云数据生成平滑三角网格的方法。它基于泊松方程,通过全局优化的方法生成表面,能够有效处理噪声和不完整的点云数据。

11.2.1 原理

  1. 法线估计

    • 首先,从点云数据中估计每个点的法线方向。这一步通常使用邻域搜索算法,如KD树搜索。
  2. 泊松方程
    泊松重建的核心是求解泊松方程。泊松方程是一种偏微分方程,形式为:

∇ ⋅ V = ρ \nabla \cdot \mathbf{V} = \rho V=ρ

其中, V \mathbf{V} V 是一个向量场, ρ \rho ρ 是一个标量场。在泊松重建中, V \mathbf{V} V 是由点云的法线生成的向量场, ρ \rho ρ 是点云的散度。

  1. 构建八叉树

    • 为了高效地求解泊松方程,使用八叉树(Octree)对点云进行分层表示。八叉树将空间递归地划分为八个子空间,直到达到指定的深度。
  2. 求解泊松方程

    • 在八叉树的每个节点上,求解泊松方程。通过最小二乘法或其他数值方法,计算出每个节点的标量场值。
  3. 提取等值面

    • 使用Marching Cubes算法从标量场中提取等值面,生成三角网格。等值面是标量场中具有相同值的点的集合。
  4. 平滑和优化

    • 对生成的三角网格进行平滑和优化,以去除噪声和不规则性,生成最终的平滑表面。

11.2.2 代码示例

以下是使用Open3D库进行泊松重建的代码示例:

import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 估计法线
pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.1, max_nn=30))# 泊松重建
depth = 9  # 调整深度参数
mesh_poisson, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(pcd, depth=depth)# 可视化泊松重建结果
mesh_poisson.compute_vertex_normals()
o3d.visualization.draw_geometries([mesh_poisson], window_name=f"Poisson Reconstruction with depth={depth}")

在这里插入图片描述

11.2.3 调整深度参数

通过调整深度参数,可以控制生成的网格的细节程度:

  • 较小的深度值:生成的网格较粗糙,计算速度较快。
  • 较大的深度值:生成的网格较细致,计算速度较慢。

11.2.4 总结

泊松重建是一种从点云数据生成平滑三角网格的有效方法。它通过求解泊松方程,能够处理噪声和不完整的点云数据,生成高质量的表面。通过调整深度参数,可以控制生成网格的细节程度。

12. 最小包围盒

使用Open3D计算点云的包围盒可以通过以下两种方式:轴对齐包围盒(Axis-Aligned Bounding Box, AABB)和有向包围盒(Oriented Bounding Box, OBB)。
下面是一个示例代码,展示如何计算和可视化这两种包围盒。

  • 示例代码
import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 计算轴对齐包围盒(AABB)
aabb = pcd.get_axis_aligned_bounding_box()# 计算有向包围盒(OBB)
obb = pcd.get_oriented_bounding_box()# 设置包围盒的颜色
aabb.color = (1, 0, 0)  # 红色
obb.color = (0, 1, 0)   # 绿色# 可视化点云和包围盒
o3d.visualization.draw_geometries([pcd, aabb, obb], window_name="Bounding Boxes")

在这里插入图片描述

  • 说明
  1. 读取点云

    • 使用 o3d.io.read_point_cloud 函数读取点云数据。
  2. 计算轴对齐包围盒(AABB)

    • 使用 pcd.get_axis_aligned_bounding_box() 方法计算点云的轴对齐包围盒。AABB是一个与坐标轴对齐的最小包围盒。
  3. 计算有向包围盒(OBB)

    • 使用 pcd.get_oriented_bounding_box() 方法计算点云的有向包围盒。OBB是一个最小体积的包围盒,可以任意旋转。
  4. 设置包围盒的颜色

    • 通过设置 color 属性来改变包围盒的颜色,以便在可视化时区分不同的包围盒。
  5. 可视化点云和包围盒

    • 使用 o3d.visualization.draw_geometries 函数同时可视化点云和包围盒。
  • 总结

通过上述代码,可以使用Open3D计算点云的轴对齐包围盒和有向包围盒,并进行可视化。这对于点云数据的分析和处理非常有用。

13. 凸包

使用Open3D计算点云的凸包可以通过 compute_convex_hull 方法来实现。
以下是一个示例代码,展示如何计算和可视化点云的凸包。

  • 示例代码
import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 计算凸包
hull, _ = pcd.compute_convex_hull()# 设置凸包的颜色
hull.paint_uniform_color([1, 0, 0])  # 红色# 可视化点云和凸包
o3d.visualization.draw_geometries([pcd, hull], window_name="Convex Hull")

在这里插入图片描述

  • 说明
  1. 读取点云

    • 使用 o3d.io.read_point_cloud 函数读取点云数据。
  2. 计算凸包

    • 使用 pcd.compute_convex_hull() 方法计算点云的凸包。该方法返回一个三角网格表示的凸包和一个索引数组(这里我们只关心凸包)。
  3. 设置凸包的颜色

    • 使用 hull.paint_uniform_color([1, 0, 0]) 方法将凸包的颜色设置为红色,以便在可视化时区分凸包和点云。
  4. 可视化点云和凸包

    • 使用 o3d.visualization.draw_geometries 函数同时可视化点云和凸包。
  • 总结

    • 通过上述代码,可以使用Open3D计算点云的凸包,并进行可视化。这对于点云数据的分析和处理非常有用,特别是在需要了解点云的外部形状时。

14. 体素化

使用Open3D计算点云的体素化可以通过 voxel_down_sample 方法来实现。体素化是将点云划分为固定大小的立方体(体素),并用每个体素内的点的中心点来代表该体素。
以下是一个示例代码,展示如何对点云进行体素化处理并进行可视化。

  • 示例代码
import open3d as o3d# 读取点云
pcd = o3d.io.read_point_cloud("doll_1.ply")# 设置体素大小
voxel_size = 0.05# 进行体素化
voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=voxel_size)# 可视化原始点云和体素化后的点云
o3d.visualization.draw_geometries([pcd], window_name="Original Point Cloud")
o3d.visualization.draw_geometries([voxel_grid], window_name="Voxelized Point Cloud")

在这里插入图片描述
在这里插入图片描述

  • 说明
  1. 读取点云

    • 使用 o3d.io.read_point_cloud 函数读取点云数据。
  2. 设置体素大小

    • 定义体素的大小(例如 voxel_size = 0.05),该值决定了每个体素的边长。
  3. 进行体素化

    • 使用 o3d.geometry.VoxelGrid.create_from_point_cloud(pcd, voxel_size=voxel_size) 方法对点云进行体素化处理。该方法返回一个 VoxelGrid 对象,表示体素化后的点云。
  4. 可视化原始点云和体素化后的点云

    • 使用 o3d.visualization.draw_geometries 函数分别可视化原始点云和体素化后的点云。
  • 总结

    • 通过上述代码,可以使用Open3D对点云进行体素化处理,并进行可视化。体素化处理可以有效地减少点云数据的数量,同时保留点云的整体结构,对于点云数据的分析和处理非常有用。

相关文章:

Open3D 学习指南 (持续学习)

本章仅为个人学习整理。 Open3D: https://www.open3d.org/ Github repo: https://github.com/isl-org/Open3D 1. 概述 Open3D 是一个开源库&#xff0c;旨在为 3D 数据处理提供高效且易用的工具。它由 Intel 开发和维护&#xff0c;支持多种 3D 数据处理任务&#xff0c;如…...

端口安全测试全方位指南:风险、流程与防护策略

在数字化时代&#xff0c;网络安全至关重要&#xff0c;而端口安全作为网络防护的前沿阵地&#xff0c;稍有疏忽就可能为恶意攻击者打开入侵的大门。以下为您详细阐述端口安全测试的全面流程、核心风险点、应对策略及防护建议。 一、测试前的周密筹备 &#xff08;一&#xf…...

3dsmax烘焙光照贴图然后在unity中使用

效果预览 看不清[完蛋&#xff01;] 实现步骤 使用 软件 软体名称地址photoshophttps://www.adobe.com/products/photoshop.htmlunity3Dhttps://unity.com/3dsmaxhttps://www.autodesk.com.cn/products/3ds-max/free-trialpacker-iohttps://www.uv-packer.com/HDR 贴图地址…...

GCN从理论到实践——基于PyTorch的图卷积网络层实现

Hi&#xff0c;大家好&#xff0c;我是半亩花海。图卷积网络&#xff08;Graph Convolutional Network, GCN&#xff09;是一种处理图结构数据的深度学习模型。它通过聚合邻居节点的信息来更新每个节点的特征表示&#xff0c;广泛应用于社交网络分析、推荐系统和生物信息学等领…...

Ollama存在安全风险的情况通报及解决方案

据清华大学网络空间测绘联合研究中心分析&#xff0c;开源跨平台大模型工具Ollama默认配置存在未授权访问与模型窃取等安全隐患。鉴于目前DeepSeek等大模型的研究部署和应用非常广泛&#xff0c;多数用户使用Ollama私有化部署且未修改默认配置&#xff0c;存在数据泄露、算力盗…...

大模型在高血压预测及围手术期管理中的应用研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的 1.3 国内外研究现状 二、大模型预测高血压的原理与方法 2.1 常用大模型介绍 2.2 数据收集与预处理 2.3 模型训练与验证 三、术前风险预测与手术方案制定 3.1 术前风险因素分析 3.2 大模型预测术前风险的方法与结果 …...

网络安全rt是什么意思

1.什么时EDR :完全不同以往的端点被防护思路&#xff0c;而是通过云端威胁情报&#xff0c;机器学习&#xff0c;异常行为分析&#xff0c;攻击指示器等方式&#xff0c;主动发现来自外部或内部的安全威胁 。并进行自动化的阻止&#xff0c;取证&#xff0c;补救和溯源从而有效…...

数据结构篇—栈(stack)

一、引入 在数学史上有这样一个经典问题——汉诺塔问题。 通过动图演示我们发现每一个圆片的运动是什么样的呢&#xff1f; 我们发现&#xff0c;第一个放入的最大圆片将位于整个塔的最底端。所以若想将最大圆片拿出来&#xff0c;就得将压在它身上的所有圆片先按顺序取出才能将…...

python3.13安装教程【2025】python3.13超详细图文教程(包含安装包)

文章目录 前言一、python3.13安装包下载二、Python 3.13安装步骤三、Python3.13验证 前言 本教程将为你详细介绍 Python 3.13 python3.13安装教程&#xff0c;帮助你顺利搭建起 Python 3.13 开发环境&#xff0c;快速投身于 Python 编程的精彩实践中。 一、python3.13安装包下…...

动态内存分配

动态内存分配 1. malloc1.1函数原型1.2参数1.3特点1.4注意事项 2.calloc2.1函数原型2.2参数2.3特点2.4注意事项 3.realloc3.1函数原型3.2参数3.3特点3.4注意事项 4.free4.1 函数原型4.2参数4.3特点 结语 在 C 语言中&#xff0c;主要使用标准库函数 <stdlib.h> 中的几个函…...

物联网设备数据割裂难题:基于OAuth2.0的分布式用户画像系统设计!格行代理是不是套路?2025有什么比较好的副业?低成本的创业好项目有哪些?

一、行业基本面&#xff1a;双赛道增长逻辑验证 1.1 随身WiFi市场&#xff1a;场景红利与技术博弈 移动办公、户外直播等场景推动随身WiFi需求持续增长&#xff0c;格行核心的三网切换技术&#xff08;移动/联通/电信自动择优&#xff09;有效解决单一运营商信号覆盖盲区问题&…...

17.10 LangSmith Evaluation 深度实战:构建智能评估体系驱动大模型进化

LangSmith Evaluation 深度实战:构建智能评估体系驱动大模型进化 关键词:LangSmith 评估体系, 大模型质量评估, 自动化评测流水线, 多维度指标分析, 生产环境模型监控 1. 评估体系设计哲学 LangSmith Evaluation 采用 规则评估+模型评估+人工反馈 三位一体的评估框架: #me…...

Gravitino SparkConnector 实现原理

Gravitino SparkConnector 实现原理 本文参考了官网介绍&#xff0c;想看官方解析请参考 官网地址 本文仅仅介绍原理 文章目录 Gravitino SparkConnector 实现原理背景知识-Spark Plugin 介绍(1) **插件加载**(2) **DriverPlugin 初始化**(3) **ExecutorPlugin 初始化**(4) *…...

前端开发好用的AI工具介绍

以下是前端开发中提升效率的 AI 工具 推荐&#xff0c;涵盖代码生成、UI设计、调试优化等场景&#xff1a; 一、代码生成与辅助工具 工具名称特点适用场景GitHub Copilot基于 OpenAI&#xff0c;智能代码补全&#xff08;支持 JS/TS/React/Vue&#xff09;快速生成代码片段、函…...

Linux的用户与权限--第二天

认知root用户&#xff08;超级管理员&#xff09; root用户用于最大的系统操作权限 普通用户的权限&#xff0c;一般在HOME目录内部不受限制 su与exit命令 su命令&#xff1a; su [-] 用户名 -符号是可选的&#xff0c;表示切换用户后加载环境变量 参数为用户名&#xff0c…...

COUNT(CASE WHEN ... THEN ... END)详解

在 SQL 查询中&#xff0c;COUNT(CASE WHEN ... THEN ... END) 是一种常见的用法&#xff0c;用于统计满足特定条件的记录数。具体例子&#xff1a; # sexType 2表示女生 COUNT(CASE WHEN h_employee.sexType 2 THEN 1 END) AS 女员工人数解释 CASE WHEN ... THEN ... END&a…...

音视频入门基础:RTP专题(14)——FFmpeg源码中,对H.264的各种RTP有效载荷结构的解析

一、引言 由《音视频入门基础&#xff1a;RTP专题&#xff08;10&#xff09;——FFmpeg源码中&#xff0c;解析RTP header的实现》可以知道&#xff0c;FFmpeg源码的rtp_parse_packet_internal函数的前半部分实现了解析某个RTP packet的RTP header的功能。而在解析完RTP head…...

FPGA——4位全加器及3-8译码器的实现

文章目录 一、全加器1、Verilog实现四位全加器2、下载测试 二、3-8译码器1、Verilog实现3-8译码器2、7段数码管显示3-8译码器 三、总结四、参考资料 一、全加器 全加器的定义&#xff1a; 全加器英语名称为full-adder&#xff0c;是用门电路实现两个二进制数相加并求出和的组合…...

软考中级-数据库-3.4 数据结构-图

图的定义 一个图G(Graph)是由两个集合:V和E所组成的&#xff0c;V是有限的非空顶点(Vertex)集合&#xff0c;E是用顶点表示的边(Edge)集合&#xff0c;图G的顶点集和边集分别记为V(G)和E(G)&#xff0c;而将图G记作G(V&#xff0c;E)。可以看出&#xff0c;一个顶点集合与连接这…...

软考中级-数据库-3.3 数据结构-树

定义:树是n(n>=0)个结点的有限集合。当n=0时称为空树。在任一非空树中,有且仅有一个称为根的结点:其余结点可分为m(m>=0)个互不相交的有限集T1,T2,T3...,Tm…,其中每个集合又都是一棵树,并且称为根结点的子树。 树的相关概念 1、双亲、孩子和兄弟: 2、结点的度:一个结…...

Win11被背刺,官方泄露免费激活方法

AI已经成为科技圈的主旋律了&#xff0c;在PC圈的龙头微软也不例外。 但最近喜欢背刺用户、极力推崇AI的微软被自家产品背刺了一把。 罪魁祸首就是Microsoft Copilot&#xff0c;如果向Microsoft Copilot提问&#xff0c;是否可以帮忙提供激活Windows11的脚本。 Copilot会立马…...

第十天-字符串:编程世界的文本基石

在编程的广阔领域中&#xff0c;字符串是极为重要的数据类型&#xff0c;它就像一座桥梁&#xff0c;连接着人类的自然语言和计算机能够理解与处理的数字信息。下面&#xff0c;让我们深入探索字符串的世界。 一、字符串简介 字符串是由零个或多个字符组成的有序序列&#xff…...

CentOS7 安装Redis 6.2.6 详细教程

本文主要介绍CentOS7系统下安装Redis6.2.6的详细教程。 1.安装依赖 redis是基于C语言开发&#xff0c;因此想要在服务器上运行redis需要验证是否安装了gcc&#xff0c;没有安装gcc则需先安装 查看是否安装gcc gcc -v如果没有安装gcc&#xff0c;则通过如下命令安装 yum in…...

VsCode使用

vscode前端vue项目启动&#xff1a;Vue项目的创建启动及注意事项-CSDN博客 vscode使用教程&#xff1a;史上最全vscode配置使用教程 - 夏天的思考 - 博客园 vscode如何从git拉取代码&#xff1a;vscode如何从git拉取代码 • Worktile社区...

mac上最好的Python开发环境之Anaconda+Pycharm

文章目录 一、前言 1. Anaconda介绍2. Pycharm介绍 编码协助项目代码导航代码分析Python重构支持Django框架集成版本控制 二、下载Anaconda和Pycharm 1. 下载Anaconda2. 下载Pycharm 三、安装Anaconda和Pycharm 1. 安装Anaconda2. 安装Pycharm 一、前言 1. Anaconda介绍 …...

防火墙旁挂组网双机热备负载均衡

一&#xff0c;二层交换网络&#xff1a; 使用MSTPVRRP组网形式 VLAN 2--->SW3为主,SW4 作为备份 VLAN 3--->SW4为主,SW3 作为备份 MSTP 设计 --->SW3 、 4 、 5 运行 实例 1 &#xff1a; VLAN 2 实例 2 &#xff1a; VLAN 3 SW3 是实例 1 的主根&#xff0c;实…...

Docker 学习(三)——数据管理

容器中的管理数据主要有两种方式&#xff1a; 数据卷 &#xff08;Data Volumes&#xff09;&#xff1a; 容器内数据直接映射到本地主机环境&#xff1b; 数据 卷容器&#xff08; Data Volume Containers&#xff09;&#xff1a; 使用特定容器维护数据卷 1.数据卷 数据卷…...

中间件专栏之MySQL篇——MySQL缓存策略

本文所说的MySQL缓存策略与前文提到的buffer pool不同&#xff0c;那是MySQL内部自己实现的&#xff0c;本问所讲的缓存策略是使用另一个中间件redis来缓存MySQL中的热点数据。 一、为什么需要MySQL缓存方案 缓存用户定义的热点数据&#xff0c;用户可以直接从缓存中获取热点…...

高频 SQL 50 题(基础版)_196. 删除重复的电子邮箱

高频 SQL 50 题&#xff08;基础版&#xff09;_196. 删除重复的电子邮箱 思路 思路 DELETE p1 FROM Person p1,Person p2 WHEREp1.Email p2.Email AND p1.Id > p2.Id...

github进不去,一直显示错误

1、进入网址Dns检测|Dns查询 - 站长工具 2、复制检测出来的任意一个ip 3、打开电脑的文件夹&#xff1a;C:\Windows\System32\drivers\etc 下的hosts文件下复制这个ip地址 20.205.243.166 4、winr 打开cmd&#xff0c;输入ipconfig/flushdns ipconfig/flushdns出现这个就可以…...

MWC 2025|美格智能发布基于高通®X85 5G调制解调器及射频的新一代5G-A通信模组SRM819W

3月3日&#xff0c;在MWC 2025世界移动通信大会上&#xff0c;美格智能正式推出基于高通X85调制解调器及射频的新一代5G-A通信模组SRM819W&#xff0c;集5G-A、毫米波、AI加持的网络优化等最前沿的通信技术&#xff0c;成为行业首批搭载高通X85的5G通信模组产品&#xff0c;将助…...

【零基础到精通Java合集】第十集:List集合框架

课程标题:List集合框架(15分钟) 目标:掌握List接口核心实现类(ArrayList/LinkedList)的使用与场景选择,熟练操作有序集合 0-1分钟:List概念引入 以“购物清单”类比List特性:元素有序(添加顺序)、可重复、支持索引访问。说明List是Java集合框架中最常用的数据结构…...

《今日-AI-编程-人工智能日报》

一、AI行业动态 荣耀发布“荣耀阿尔法战略” 荣耀在“2025世界移动通信大会”上宣布&#xff0c;将从智能手机制造商转型为全球领先的AI终端生态公司&#xff0c;并计划未来五年投入100亿美元建设AI设备生态。荣耀展示了基于GUI的个人移动AI智能体&#xff0c;并推出多款AI终端…...

在 MyBatis 中,若数据库字段名与 SQL 保留字冲突解决办法

在 MyBatis 中&#xff0c;若数据库字段名与 SQL 保留字冲突&#xff0c;可通过以下方法解决&#xff1a; 目录 一、使用转义符号包裹字段名二、通过别名映射三、借助 MyBatis-Plus 注解四、全局配置策略&#xff08;辅助方案&#xff09;最佳实践与注意事项 一、使用转义符号…...

从基础到实践(十):MOS管的全面解析与实际应用

MOS管&#xff08;金属-氧化物半导体场效应晶体管&#xff09;是现代电子技术的基石&#xff0c;凭借高输入阻抗、低功耗和易集成特性&#xff0c;成为数字电路、电源管理和信号处理的核心元件。从微处理器到新能源汽车电驱系统&#xff0c;其高效开关与放大功能支撑了计算机、…...

电源测试系统有哪些可以利用AI工具的科技??

AI技术的发展对电源模块测试系统的影响是深远的&#xff0c;不仅协助系统提升了测试效率和精度&#xff0c;还推动了测试方法的创新和智能化。那么在电源测试系统中哪些模块可以利用AI工具实现自动化测试? 1. 自动化测试与效率提升 智能测试流程优化 AI算法可以自动优化测试…...

RabbitMQ 最新版:安装、配置 与Java 接入详细教程

目录 一、RabbitMQ 简介二、RabbitMQ 的安装1. 安装 Erlang下载 Erlang安装 Erlang2. 安装 RabbitMQ下载 RabbitMQ安装 RabbitMQ3. 配置环境变量4. 启用管理插件三、RabbitMQ 的配置1. 创建用户和设置权限2. 配置文件四、Java 接入 RabbitMQ1. 添加依赖2. 创建连接3. 创建通道4…...

股市现期驱动因子

在股票投资中&#xff0c;我们把驱动股市收益的基本元素称为基本因素&#xff1a; 例如资产负债、现金流量 从短期来看&#xff0c;股市的上涨和下跌基于市场情绪&#xff0c;它更依赖于投资者的期望&#xff0c;投它涨的人多&#xff0c;它就涨。从长期来看&#xff0c;股市…...

物联网中的气象监测设备具备顶级功能

物联网中的气象监测设备具备顶级功能时&#xff0c;通常集成GPS、数据上报和预警系统&#xff0c;以确保精准监测和及时响应。以下是这些功能的详细说明&#xff1a; 1. GPS定位 精准定位&#xff1a;GPS模块提供设备的精确地理位置&#xff0c;确保数据与具体位置关联&#…...

算法1-4 凌乱的yyy / 线段覆盖

题目描述 现在各大 oj 上有 n 个比赛&#xff0c;每个比赛的开始、结束的时间点是知道的。 yyy 认为&#xff0c;参加越多的比赛&#xff0c;noip 就能考的越好&#xff08;假的&#xff09;。 所以&#xff0c;他想知道他最多能参加几个比赛。 由于 yyy 是蒟蒻&#xff0c…...

gn学习存档

以下答案均由deepseek提供&#xff0c;仅作学习存档。 1. 举例说明action和action_foreach区别 场景设定 假设需要处理一组文件&#xff1a; 输入文件&#xff1a;src/data/file1.txt, src/data/file2.txt, src/data/file3.txt处理逻辑&#xff1a;将每个 .txt 文件转换为 …...

SQL注入练习场:PHPStudy+SQLI-LABS靶场搭建教程(零基础友好版)

注意&#xff1a;文中涉及演示均为模拟测试&#xff0c;切勿用于真实环境&#xff0c;任何未授权测试都是违法行为&#xff01; 一、环境准备 下载PHPStudy 官网下载地址&#xff1a;https://www.xp.cn/php-study&#xff08;选择Windows版&#xff09; 安装时建议选择自定…...

python学习笔记——Thread常用方法

Thread对象中的一些方法&#xff1a; 以前说过多线程&#xff0c;用到threading模块中的Thread对象&#xff0c;其中的start和run方法比较熟悉了&#xff0c;start&#xff08;&#xff09;是重载了Thread对象中的run方法&#xff0c;其实作用还是&#xff0c;当执行这个start…...

2024年数学SCI2区TOP:雪雁算法SGA,深度解析+性能实测

目录 1.摘要2.算法原理3.结果展示4.参考文献5.代码获取 1.摘要 本文提出了一种雪雁算法&#xff08;SGA&#xff09;&#xff0c;该算法借鉴了雪鹅的迁徙行为&#xff0c;并模拟了其迁徙过程中常见的“人字形”和“直线”飞行模式。 2.算法原理 雪雁以其卓越的长途迁徙能力和…...

Kubernetes 指令备忘清单

文章目录 查看资源信息节点容器组命名空间无状态服务守护进程集事件服务帐户日志副本集角色保密字典配置项路由持久卷持久卷声明存储类多个资源 变更资源属性污点标签维护/可调度清空节点节点/容器组无状态/命名空间服务守护进程集服务账号注释 添加资源创建容器组创建服务创建…...

servlet tomcat

在spring-mvc demo程序运行到DispatcherServlet的mvc处理 一文中&#xff0c;我们实践了浏览器输入一个请求&#xff0c;然后到SpringMvc的DispatcherServlet处理的整个流程. 设计上这些都是tomcat servlet的处理 那么究竟这是怎么到DispatcherServlet处理的&#xff0c;本文将…...

在 Ubuntu 系统 22.04 上安装 Docker

在 Ubuntu 系统 22.04 上安装 Docker 在 Ubuntu 系统 22.04 上安装 Docker1. 更新系统包2. 安装依赖工具3. 添加 Docker 官方 GPG 密钥4. 添加 Docker 的 APT 仓库5. 安装 Docker Engine6. 启动并设置 Docker 服务7. 验证安装8. 配置非 Root 用户权限&#xff08;可选&#xf…...

一分钟理解Mybatis 里面的缓存机制

​ MyBatis 是一个流行的 Java 持久层框架&#xff0c;它简化了数据库操作。MyBatis 提供了强大的缓存机制&#xff0c;用于提升性能&#xff0c;减少数据库的访问次数。MyBatis 的缓存机制分为一级缓存和二级缓存。 ​ 该图展示了用户通过 SqlSession 发起查询请求&#xff0c…...

【我的Android进阶之旅】如何使用NanoHttpd在Android端快速部署一个HTTP服务器?

文章目录 开篇:程序员的"摸鱼神器"?一、为什么选择NanoHttpd?二、五分钟极速上车指南2.1 ▶ 第一步:引入依赖的哲学2.2 ▶ 第二步:创建服务器类:继承大法好2.3 ▶ 第三步:启动服务的仪式感三、高级玩法:让服务器不再单调3.1 🔥 场景1:变身文件服务器3.2 �…...

PyCharm 无法识别 Conda 环境的解决方案

一、问题分析 当在最新版 PyCharm (2024.3) 中配置 Conda 环境时&#xff0c;可能会出现以下典型错误&#xff1a; 找不到 Conda 可执行文件 我在网上找了很多解决办法&#xff0c;都没有有效解决这个问题&#xff0c;包括将环境路径替换为 .bat 文件和查找 python.exe 文件…...