OBB-最小外接矩形包围框-原理-代码实现

前言

  • 定义:OBB是相对于物体方向对齐的包围盒,不再局限于坐标轴对齐,因此包围点云时更加紧密。
  • 优点:能够更好地贴合物体形状,减少空白区域。
  • 缺点:计算较为复杂,需要计算物体的主方向,进行旋转和缩放变换。

Pasted image 20240920104735

算法原理

实现OBB通常涉及以下步骤:

  1. 计算凸包(Convex Hull)
  2. 使用主成分分析(Principal Component Analysis,简称PCA)找到最佳的旋转方向
  3. 根据这些方向确定包围盒的边界

步骤一:计算凸包

凸包是一组点的最小凸多边形,包含所有点,并且所有点都在多边形的边界上或内部。可以使用scipy库中的ConvexHull函数来计算。

import numpy as np
from scipy.spatial import ConvexHull
# 生成数据
mean = [0, 0]
cov = [[3, 1], [1, 2]]
points = np.random.multivariate_normal(mean, cov, 300)

# 计算凸包
convex_hull = ConvexHull(points)
# 可视化
plt.figure(figsize=(10, 8))
plt.scatter(points[:, 0], points[:, 1], s=10, alpha=0.5, label='Points')

Pasted image 20240920105021
图 展示了图包得到的最外层点及其点之间的连线

步骤二:主成分分析(PCA)

PCA可以帮助我们找到数据的主方向。通过分析凸包顶点的协方差矩阵,我们可以得到主要的方向,这些方向定义了OBB的方向。

from sklearn.decomposition import PCA

pca = PCA(n_components=2)
pca.fit(points[convex_hull.vertices])  # 仅对凸包顶点进行PCA

步骤三:根据PCA方向计算OBB

根据PCA的结果,我们可以确定OBB的方向。然后,我们需要计算在这些方向上点的投影,以此来确定矩形的边界。

# OBB计算函数
def compute_obb(points):
    # 使用PCA找到数据的主成分方向
    pca = PCA(n_components=2)
    pca.fit(points)
    
    # 得到主轴
    eigen_vectors = pca.components_
    eigen_values = pca.explained_variance_
    
    # 将点投影到主轴上
    projected_points = np.dot(points, eigen_vectors.T)
    
    # 计算包围盒的边界
    min_proj = np.min(projected_points, axis=0)
    max_proj = np.max(projected_points, axis=0)
    
    # 通过主轴和包围盒的边界重构OBB顶点
    obb_vertices = np.array([
        [min_proj[0], min_proj[1]],
        [min_proj[0], max_proj[1]],
        [max_proj[0], max_proj[1]],
        [max_proj[0], min_proj[1]]
    ])
    
    # 将OBB顶点从PCA空间转换回原始空间
    obb_vertices = np.dot(obb_vertices, eigen_vectors)
    
    return obb_vertices
obb_vertices = compute_obb(points)

上述代码定义了计算OBB的整个过程,从计算凸包到应用PCA,最后确定边界框的角。每步都是基于数学原理进行建模,确保能找到最合适的包围盒。
Pasted image 20240920104735

完整代码

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from scipy.spatial import ConvexHull

# OBB计算函数
def compute_obb(points):
    # 使用PCA找到数据的主成分方向
    pca = PCA(n_components=2)
    pca.fit(points)
    
    # 得到主轴
    eigen_vectors = pca.components_
    eigen_values = pca.explained_variance_
    
    # 将点投影到主轴上
    projected_points = np.dot(points, eigen_vectors.T)
    
    # 计算包围盒的边界
    min_proj = np.min(projected_points, axis=0)
    max_proj = np.max(projected_points, axis=0)
    
    # 通过主轴和包围盒的边界重构OBB顶点
    obb_vertices = np.array([
        [min_proj[0], min_proj[1]],
        [min_proj[0], max_proj[1]],
        [max_proj[0], max_proj[1]],
        [max_proj[0], min_proj[1]]
    ])
    
    # 将OBB顶点从PCA空间转换回原始空间
    obb_vertices = np.dot(obb_vertices, eigen_vectors)
    
    return obb_vertices

np.random.seed(42)
mean = [0, 0]
cov = [[3, 1], [1, 2]]
points = np.random.multivariate_normal(mean, cov, 300)

# 计算OBB顶点
obb_vertices = compute_obb(points)

# 计算凸包
convex_hull = ConvexHull(points)

# 可视化点云、OBB和凸包
plt.figure(figsize=(10, 8))
plt.scatter(points[:, 0], points[:, 1], s=10, alpha=0.5, label='Points')

# 绘制OBB
obb_polygon = np.vstack([obb_vertices, obb_vertices[0]])  # 闭合OBB多边形
plt.plot(obb_polygon[:, 0], obb_polygon[:, 1], 'r-', linewidth=2, label='OBB')

# 绘制凸包
for simplex in convex_hull.simplices:
    plt.plot(points[simplex, 0], points[simplex, 1], 'g--', linewidth=1)

plt.plot(points[convex_hull.vertices, 0], points[convex_hull.vertices, 1], 'g-', linewidth=2, label='Convex Hull')
plt.scatter(points[convex_hull.vertices, 0], points[convex_hull.vertices, 1], color='green')

plt.title('OBB and Convex Hull Visualization')
plt.xlabel('X-axis')
plt.ylabel('Y-axis')
plt.legend()
plt.axis('equal')
plt.grid(True)
plt.show()

利用Open3D使用OBB算法

import open3d as o3d
import numpy as np

# 读取点云 txt 文件,假设文件名为 'point_cloud.txt'
file_path = './data/5.txt'

# 使用 numpy 读取数据
point_cloud_data = np.loadtxt(file_path)

# 提取点的坐标 (x, y, z) 和颜色 (r, g, b)
points = point_cloud_data[:, :3]
colors = point_cloud_data[:, 3:] / 255.0  # Open3D expects colors in [0, 1] range

# 创建 Open3D 点云对象
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(points)
pcd.colors = o3d.utility.Vector3dVector(colors)


# 计算AABB(轴对齐包围盒)
obb = pcd.get_oriented_bounding_box()
axis = o3d.geometry.TriangleMesh.create_coordinate_frame()


# 可视化点云和AABB
obb.color = (0, 1, 0)  
o3d.visualization.draw_geometries([pcd,obb])


可视化结果:
Pasted image 20240920105437

03-20
### OBB(Oriented Bounding Box)的概念及其使用方法 #### 什么是OBB? 方向包围盒(Oriented Bounding Box, OBB)是一种三维空间中的矩形体,其边不一定平行于坐标轴。它通常被用来表示物体的最小体积外接矩形,在游戏开发、虚拟现实以及计算机图形学领域广泛应用于碰撞检测和物理模拟中[^1]。 #### 实现OBB的关键要素 为了实现OBB并将其用于实际场景,需要考虑以下几个方面: 1. **定义OBB结构** 在C#或其他编程语言中,可以通过类来描述OBB的核心属性,包括中心点位置、旋转矩阵或欧拉角、半径向量等参数。 下面是一个简单的OBB类定义示例: ```csharp public class OrientedBoundingBox { public Vector3 Center; // 中心点 public Quaternion Rotation; // 旋转角度 (四元数形式) public Vector3 HalfExtents; // 半宽高深 public OrientedBoundingBox(Vector3 center, Quaternion rotation, Vector3 halfExtents) { this.Center = center; this.Rotation = rotation; this.HalfExtents = halfExtents; } } ``` 2. **计算OBB的方向** 方向可以由旋转矩阵或者四元数决定。通过这些数学工具,能够精确控制OBB相对于世界坐标的朝向[^3]。 3. **生成OBB的方法** 对于一组离散点云数据,可以采用主成分分析(Principal Component Analysis, PCA)提取特征向量作为OBB的主要轴线方向,并以此构建最优拟合的OBB实例[^2]。 4. **碰撞检测算法** 常见的OBB间碰撞检测依赖分离轴定理(Separating Axis Theorem),即如果存在一条直线使得两个OBB投影到该直线上不重叠,则它们彼此无接触。具体实现涉及多个候选分隔轴上的距离比较操作。 #### C#代码片段展示基本功能 以下是利用上述理论框架编写的一个简化版OBB初始化与显示逻辑: ```csharp using UnityEngine; public static class OBBDemo { public static void CreateAndShow(OrientedBoundingBox obb){ var points = new List<Vector3>(); // 构造八个顶点... for(int i=-1;i<=1;i+=2){ for(int j=-1;j<=1;j+=2){ for(int k=-1;k<=1;k+=2){ var localPoint=new Vector3(i,j,k)*obb.HalfExtents; var worldPoint=Quaternion.Euler(obb.Rotation.eulerAngles)*localPoint+obb.Center; points.Add(worldPoint); Debug.DrawLine(points.Last(),points.Count>1?points[points.Count-2]:worldPoint.Color(Color.red)); } } } foreach(var p in points.TakeWhile((_,idx)=>idx<7)){ Debug.DrawLine(p,points[(int)(p-points.First()).magnitude%8].Color(Color.green)); } } } ``` 此脚本假设Unity引擎环境下运行,其中`Debug.DrawLine()`函数负责绘制线条辅助观察效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值