【三维视觉】空间点集的最小包围盒计算

news2024/11/27 14:29:22

0 问题描述

假设有一个空间点集,不重合的点数有N个。
N=1时,最小包围盒是一个点:中心为其本身,半径无穷小
N=2时,最小包围盒是一个圆:中心为连线中点,半径为边长一半
N=3时,不共线的三点连成一个三角形,锐角和直角三角形其最小包围圆就是其外接圆。钝角三角形以最长边作为直径的圆是最小外接圆
N=4时,不共面的四点组成一个四面体,其最小包围球?如何计算?应该不一定是他的外接球。
那么
N>4,…

1 包围盒介绍

常见的包围盒种类有有以下4种:

①轴对齐包围盒AABB(Axis-aligned bounding box)
②包围球(Bounding Sphere)
③有向包围盒OBB(Oriented bounding box)
④固定方向凸包FDH(Fixed directions hulls或k-DOP)

1.1 AABB轴对齐包围盒

它被定义为包含该对象,且边平行于坐标轴的最小六面体。故描述一个AABB,仅需六个标量。AABB构造比较简单,存储空间小,但紧密性差,尤其对不规则几何形体,冗余空间很大,当对象旋转时,无法对其进行相应的旋转。处理对象是刚性并且是凸的,不适合包含软体变形的复杂的虚拟环境情况。

1.2 BS包围球

它被定义为包含该对象的最小的球体。确定包围球,首先需分别计算组成对象的基本几何元素集合中所有元素的顶点的x,y,z坐标的均值以确定包围球的球心,再由球心与三个最大值坐标所确定的点间的距离确定半径r。包围球的碰撞检测主要是比较两球间半径和与球心距离的大小。

1.3 OBB有向包围盒

OBB是较为常用的包围盒类型。它是包含该对象且相对于坐标轴方向任意的最小的长方体。OBB最大特点是它的方向的任意性,这使得它可以根据被包围对象的形状特点尽可能紧密的包围对象,但同时也使得它的相交测试变得复杂。OBB包围盒比AABB包围盒和包围球更加紧密地逼近物体,能比较显著地减少包围体的个数,从而避免了大量包围体之间的相交检测。但OBB之间的相交检测比AABB或包围球体之间的相交检测更费时。

1.4 FDH固定方向凸包

FDH(k-DOP)是一种特殊的凸包,继承了AABB简单性的特点,但其要具备良好的空间紧密度,必须使用足够多的固定方向。被定义为包含该对象且它的所有面的法向量都取自一个固定的方向(k个向量)集合的凸包。FDH比其他包围体更紧密地包围原物体,创建的层次树也就有更少的节点,求交检测时就会减少更多的冗余计算,但相互间的求交运算较为复杂。

2 包围盒计算算法

2.1 BS包围球计算算法

2.2.1 naive算法

一个最简单的思路就是,计算空间顶点在X、Y、Z方向上的最大值和最小值,那么就可以得到8个顶点组成的包围盒。取包围球中心为包围盒中心点,而包围球半径有的人认为可以取中心点到八个顶点的最大距离——这样其实并不严密。最好还是计算中心点到所有顶点距离的最大值:

2.2.2 ritter算法

另外一种算法是一个名为ritter提出来的,所以称为ritter算法。

首先计算出X方向上距离最远的两个点,Y方向上距离最远的两个点以及Z方向上距离最远的两个点。以这三个距离最远的范围作为初始直径,这三个距离的中心点作为初始球心。

然后依次遍历所有点,判断点是否在这个包围球内。如果不在,则更新包围球。如下图所示:

2.2.3 Welzl算法

很容易用递归算法来实现:

先根据 np-1 个点,生成一个球体(递归);
判断第 np 个点是否在球体内,是,则保持球体;
否,需要根据旧的球体和第 np 个点,生成新的球体(递归)。其中第 np 个点必在新生成的球面上;
递归结束条件:当一个点、两个点时,可直接生成球体;三个点都在球面上,则生成三角形的外接球;

3、现成代码库(包)

4、外接球与最小包围球的区别

注意外接球和我理解的最小包围球是不一样的。
黄色的是最小包围球,绿色的是外接球
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5、四点外接球与最小包围球的计算

import numpy as np
import matplotlib.pyplot as plt


def get_min_sphere_4points(points):
    """
    Get the minimum radius of a circumscribed sphere that encloses all the points
    """
    def minimum_enclosing_sphere_3points(triangle):
        # Compute the circumcenter of the triangle
        a, b, c = triangle
        ab = b - a
        ac = c - a
        ab_cross_ac = np.cross(ab, ac)
        ab_cross_ac_norm_sq = np.dot(ab_cross_ac, ab_cross_ac)
        if ab_cross_ac_norm_sq == 0:
            # Points are colinear, return a point and radius of infinity
            return a, np.inf
        ab_norm_sq = np.dot(ab, ab)
        ac_norm_sq = np.dot(ac, ac)
        circumcenter = a + (np.cross(ab_norm_sq * ac - ac_norm_sq * ab, ab_cross_ac) / (2 * ab_cross_ac_norm_sq))
        # Calculate the radius of the circumcircle
        radius = np.linalg.norm(circumcenter - a)
        # Check if the circumcenter lies inside the triangle
        if np.all(np.logical_and(circumcenter >= a, circumcenter <= c)):
            return circumcenter, radius
        # Otherwise, the minimum enclosing sphere is the circumcircle
        else:
            center = np.mean(triangle, axis=0)
            radius = np.max(np.linalg.norm(triangle - center, axis=1))
            return center, radius
    def _min_sphere(points, center, radius):
        if len(points) == 0 or len(center) == 3:
            if len(center) == 3:
                # c1, c2, c3 = center
                # return np.array([(c1 + c2 + c3) / 3]), 0
                return minimum_enclosing_sphere_3points(center)
            elif len(center) == 2:
                c1, c2 = center
                return (c1 + c2) / 2, np.linalg.norm(c1 - c2) / 2
            elif len(center) == 1:
                return center[0], 0
            else:
                return None, 0
        else:
            p = points[0]
            points = points[1:]
            c, r = _min_sphere(points, center, radius)
            if c is None or np.linalg.norm(p - c) > r:
                center.append(p)
                c, r = _min_sphere(points, center, radius)
                center.pop()
            return c, r

    if len(points) < 4:
        raise ValueError("At least 4 points are required.")
    np.random.shuffle(points)
    center, radius = _min_sphere(points, [], 0)
    print("Center:", center)
    print("Radius:", radius)
    return center, radius


def fit_circumscribed_sphere_4points(array, tol=1e-6):
    # Check if the the points are co-linear
    D12 = array[1] - array[0]
    D12 = D12 / np.linalg.norm(D12)
    D13 = array[2] - array[0]
    D13 = D13 / np.linalg.norm(D13)
    D14 = array[3] - array[0]
    D14 = D14 / np.linalg.norm(D14)

    chk1 = np.clip(np.abs(np.dot(D12, D13)), 0., 1.)  # 如果共线,chk1=1
    chk2 = np.clip(np.abs(np.dot(D12, D14)), 0., 1.)
    # 求的是反余弦值,如果是1,反余弦值为0(弧度),乘以180/pi,就是0(度),说明共线
    if np.arccos(chk1) / np.pi * 180 < tol or np.arccos(chk2) / np.pi * 180 < tol:
        R = np.inf
        C = np.full(3, np.nan)
        return R, C

    # Check if the the points are co-planar
    n1 = np.linalg.norm(np.cross(D12, D13))
    n2 = np.linalg.norm(np.cross(D12, D14))

    chk = np.clip(np.abs(np.dot(n1, n2)), 0., 1.)
    if np.arccos(chk) / np.pi * 180 < tol:
        R = np.inf
        C = np.full(3, np.nan)
        return R, C

    # Centroid of the sphere
    A = 2 * (array[1:] - np.full(len(array) - 1, array[0]))
    b = np.sum((np.square(array[1:]) - np.square(np.full(len(array) - 1, array[0]))), axis=1)
    C = np.transpose(np.linalg.solve(A, b))

    # Radius of the sphere
    R = np.sqrt(np.sum(np.square(array[0] - C), axis=0))
    print("Center:", C)
    print("Radius:", R)

    return C, R


if __name__ == '__main__':

    # # Define the four points
    p1 = np.array([0, 0, 0])
    p2 = np.array([0, 4, 0])
    p3 = np.array([4, 0, 0])
    p4 = np.array([1, 2, 0])

    points1 = np.array([p1, p2, p3, p4])

    points1 = np.random.rand(4, 3)
    # show_tetrahedron(points1)
    center0, radius0 = fit_circumscribed_sphere_4points(points1)

    center1, radius1 = get_min_sphere_4points(points1)


    from mpl_toolkits.mplot3d import Axes3D

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    # Plot the points
    ax.scatter(points1[:, 0], points1[:, 1], points1[:, 2], c='b')
    # plot the tetrahedron
    ax.plot(points1[:, 0], points1[:, 1], points1[:, 2], c='b')

    # Plot the sphere1
    u, v = np.mgrid[0:2 * np.pi:20j, 0:np.pi:10j]
    x = center0[0] + radius0 * np.cos(u) * np.sin(v)
    y = center0[1] + radius0 * np.sin(u) * np.sin(v)
    z = center0[2] + radius0 * np.cos(v)
    ax.plot_wireframe(x, y, z, color="g")

    # Plot the sphere2
    u, v = np.mgrid[0:2 * np.pi:20j, 0:np.pi:10j]
    x = center1[0] + radius1 * np.cos(u) * np.sin(v)
    y = center1[1] + radius1 * np.sin(u) * np.sin(v)
    z = center1[2] + radius1 * np.cos(v)
    ax.plot_wireframe(x, y, z, color="y")

    # Set the axes properties
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    ax.set_aspect('equal')
    # Show the plot
    print('Showing the plot...')
    plt.show()

参考文献

最小包围球:naive算法和ritter算法
最小包围球:welzl算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/646986.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C#】并行编程实战:任务并行性(上)

在 .NET 的初始版本中&#xff0c;我们只能依赖线程&#xff08;线程可以直接创建或者使用 ThreadPool 类创建&#xff09;。ThreadPool 类提供了一个托管抽象层&#xff0c;但是开发人员仍然需要依靠 Thread 类来进行更好的控制。而 Thread 类维护困难&#xff0c;且不可托管&…

优惠券超发问题该怎么测试?

在拼夕夕面试中&#xff0c;面试官问了一连串经典的问题&#xff1a;“优惠券库存是怎么扣减的&#xff1f;开发为了解决超发优惠券问题而设计的方案&#xff0c;你了解过吗&#xff1f;你又是如何测试的呢&#xff1f;” 当时听到这些问题还挺懵的&#xff0c;没遇到过超发问…

MidJourney教程02

1.主体内容&#xff1a;高数AI你需要画什么&#xff1f;比如说&#xff0c;一个男生在电脑前画画&#xff1f; 2.环境北京&#xff1a;例如给某些地点或者物件&#xff0c;比如桌子上&#xff0c;足球场&#xff0c;水面有倒影等&#xff1f; 3.构图镜头&#xff1a;比如说强…

springboot项目外卖管理 day07-功能补充

文章目录 前端补充功能1、历史订单功能1.1、梳理过程1.2历史订单展示1.3、效果展示 2、修改/删除地址2.1、回显数据梳理过程 代码展示 2.2、修改地址梳理过程代码 2.3、删除地址梳理过程代码展示 3、再来一单功能3.1、梳理过程3.2、具体实现思路&#xff08;参考一下当初我们怎…

Linux操作系统——第四章 进程间通信

目录 进程间通信介绍 进程间通信目的 进程间通信发展 进程间通信分类 管道 System V IPC POSIX IPC 管道 什么是管道 匿名管道 管道读写规则 管道特点 命名管道 创建一个命名管道 匿名管道与命名管道的区别 命名管道的打开规则 system V共享内存 共享内存示意…

【SpringBoot】解决依赖版本不一致报错问题

哈喽大家好&#xff0c;我是阿Q。今天在开发代码的过程中&#xff0c;由于手抖&#xff0c;不知道引入了什么包依赖&#xff0c;导致项目启动一直报错&#xff0c;特写本文来记录下解决问题的经过。 文章目录 问题描述报错信息如下报错描述 解决方法总结 问题描述 报错信息如下…

vite中使用 vite- aliases 插件报错

vite 中使用 vite-aliases 插件报错 vite-aliases 介绍报错内容解决方法 vite-aliases 介绍 vite-aliases 可以帮助我们自动生成别名: 检测你当前目录下包括 src 在内的所有文件夹, 并帮助我们去生成别名。 下载 npm i vite-aliases -D 使用 import { defineConfig } from vi…

VALSE 2023 无锡线下参会个人总结 6月11日-2

VALSE2023无锡线下参会个人总结 6月11日-2 6月11日会议日程安排Workshop&#xff1a;目标检测与分割程明明&#xff1a;粒度自适应的图像感知技术张兆翔&#xff1a;基于多传感器融合的视觉物体检测与分割 Workshop&#xff1a;ChatGPT与计算机视觉白翔&#xff1a;再谈ChatGPT…

290. 单词规律

290. 单词规律 C代码&#xff1a;别人手搓的 bool wordPattern(char * pattern, char * s){char arr[301][3001];char *p strtok(s, " ");int pos 0;while(p ! NULL) {sprintf(arr[pos], "%s", p);p strtok(NULL, " ");}int len strlen(pat…

Linux环境安装Jdk图文步骤

准备工作&#xff1a; a、jdk安装包&#xff1a;百度网盘 请输入提取码&#xff0c;提取码&#xff1a;jdk8 b、远程工具&#xff0c;xshell&#xff0c;&#xff0c;electerm&#xff0c;&#xff0c;MobaXterm&#xff0c;&#xff0c;fxp&#xff0c;docker&#xff0c;宝…

软件测试V、W和H模型的优缺点汇总,零基础必看哦

目录 V模型 W模型 H模型 总结&#xff1a; 软件测试有三种模型&#xff0c;分别是V模型&#xff0c;W模型和H模型。每种模型都有自己的优点和缺点。 V模型 V模型如下图所示&#xff1a; V模型的优点 V模型明确地标识出了在开发过程中一般应完成的测试级别&#xff0c;以及…

STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

STM32-HAL库串口DMA空闲中断的正确使用方式解析SBUS信号 一. 问题描述二. 方法一——使用HAL_UART_Receive_DMA三. 方法二——使用HAL_UARTEx_ReceiveToIdle_DMA四. 方法三——使用HAL_UARTEx_ReceiveToIdle_IT&#xff08;不使用DMA&#xff09;五. 总结 一. 问题描述 能够点…

java springboot整合MyBatis-Plus 多用点Plus支持一下国人开发的东西吧

文章java springboot整合MyBatis做数据库查询操作讲述了boot项目整合MyBatis的操作方法 但现在就还有一个 MyBatis-Plus Plus是国内整合的一个技术 国内的很多人会喜欢用 特别是一些中小型公司 他们用着会比较舒服 好 然后我们打开idea 创建一个项目 选择 Spring Initializr…

(九)CSharp-数组

一、矩形数组 1、访问数组元素 class Program{static void Main(string[] args){int[] intArr1 new int[15];intArr1[2] 10;int var1 intArr1[2];int[,] intArr2 new int[5, 10];intArr2[2, 3] 7;int var2 intArr2[2, 3];int[] myIntArray new int[4];for (int i 0; i…

Git 报错 Updates were rejected because the remote contains work that you do

目录 Git 报错 Updates were rejected because the remote contains work that you do 1、命令行出现这种情况 2、idea出现同样的报错&#xff0c;解决方式同上 Git 报错 Updates were rejected because the remote contains work that you do 这个报错实在是让我受不了了&…

Kendo UI for jQuery---03.组件___网格---05.编辑---01.概述

编辑概述 编辑是剑道 UI 网格的一项基本功能&#xff0c;它允许您操作其数据的呈现方式。 网格提供以下编辑模式&#xff1a; 批量编辑 内联编辑 弹出窗口编辑 自定义编辑开始 要启用编辑&#xff1a; 熟悉剑道UI中的常见编辑概念 配置网格的数据源 通过配置定义字段schem…

PaddleOCR Windows下配置环境并测试

目录 1.PaddleOCR 介绍 1.2 PaddleOCR支持模型介绍 2.环境配置 3.PaddleOCR源码 1.PaddleOCR 介绍 PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库&#xff0c;助力开发者训练出更好的模型&#xff0c;并应用落地。 支持多种OCR相关前沿算法&#xff0c;在此基础上打…

简单的一批的DockerFile构建(内附超详细docker学习笔记)

目录 介绍 DockerFile常用保留字指令 演示自定义构建java8版本centos docker专用学习笔记 超全 介绍 总结: 从应用软件的角度来看&#xff0c;Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段&#xff0c; * Dockerfile是软件的原材料 * Docker镜像是软件…

SpringBoot参数校验入门

一、添加依赖 <!--参数校验--> <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId> </dependency> <!--lombok--> <dependency><groupId>org.projectlombok&…

few-shot object counting论文汇总

文章目录 2021OBJECT COUNTING: YOU ONLY NEED TO LOOK AT ONE 2022CounTR: Transformer-based Generalised Visual CountingFew-shot Object Counting with Similarity-Aware Feature Enhancement 2023CAN SAM COUNT ANYTHING? AN EMPIRICAL STUDY ON SAM COUNTING 2021 OBJ…