OBB的计算python实现

news2024/9/24 5:25:34

OBB的计算python实现

  • 1. 实现步骤
    • 步骤① 分解点集的xyz分量
    • 步骤② 对x、y、z这三个随机变量(一维数组)求协方差矩阵
    • 步骤③ 对步骤②中的协方差矩阵求解特征值与特征向量,特征向量构造列向量矩阵M
    • 步骤④ 将点集的几何中心平移至坐标系原点,并全部乘以M矩阵进行旋转变换
    • 步骤⑤ 将旋转变换后的点的坐标,求xMax、xMin、yMax、yMin、zMax、zMin,进而求出obb中心坐标、obb半长
    • 步骤⑥ 将obb中心坐标左乘M的逆,得到此中心坐标在原来坐标系的坐标值
    • 步骤⑦ 将步骤⑥中得到的原坐标系下的obb中心坐标平移回原处(平移向量与步骤④的平移向量刚好是相反向量)
  • 2. 代码实现OBB
    • 2.1. OOBB in 2D
    • 2.2. OBB in 3D
  • 3. 函数实现

OBB的经典生成算法:使用PCA(主成分分析)。
主成分分析有一个关键的线性代数计算步骤,即求解协方差矩阵的特征值和特征向量,这一点必须使用数值分析算法而不能用解题用的基本行变换手段,因为现代程序最大的特点就是干一些枯燥重复的事情——迭代.

在这里主要介绍三维的思路,黑盒模型:

obb的参数(中心点、三轴向量、三轴半长,以确定一个空间中的矩形)= f(点集)

1. 实现步骤

步骤① 分解点集的xyz分量

即把所有点的 x、y、z 值分别放到独立的数组中

步骤② 对x、y、z这三个随机变量(一维数组)求协方差矩阵

步骤③ 对步骤②中的协方差矩阵求解特征值与特征向量,特征向量构造列向量矩阵M

步骤④ 将点集的几何中心平移至坐标系原点,并全部乘以M矩阵进行旋转变换

步骤⑤ 将旋转变换后的点的坐标,求xMax、xMin、yMax、yMin、zMax、zMin,进而求出obb中心坐标、obb半长

步骤⑥ 将obb中心坐标左乘M的逆,得到此中心坐标在原来坐标系的坐标值

步骤⑦ 将步骤⑥中得到的原坐标系下的obb中心坐标平移回原处(平移向量与步骤④的平移向量刚好是相反向量)

最后,得到:

特征向量作为obb的三个轴朝向
obb的三个方向的半长
obb的中心坐标
其中,计算难点在于步骤③,最经典的做法是使用 Jacobi 迭代计算算法,在数值分析课程中是一节基础,Jacobi 算法还是可以优化的。
还可以使用矩阵分解算法等进行优化。

参考:简述OBB算法:使用PCA计算

2. 代码实现OBB

2.1. OOBB in 2D

是之前讨论过的关于 bounding box,如果使用 AABB 也许不是最紧密的 bounding box,更紧密的有时候也叫 OBB oriented bounding-box 或者 OOBB Object Oriented Bounding Boxes,所以建议可以使用 PCA 来计算。
在这里插入图片描述
根据文章OBB generation via Principal Component Analysis,首先有一些例子点:

(3.7, 1.7), (4.1, 3.8), (4.7, 2.9), (5.2, 2.8), (6.0, 4.0), (6.3, 3.6), (9.7, 6.3), (10.0, 4.9), (11.0, 3.6), (12.5, 6.4)

在这里插入图片描述
然后它算 covarience matrix 是:
在这里插入图片描述
用 numpy 来处理:

points = np.asarray(  [ (3.7, 1.7), (4.1, 3.8), (4.7, 2.9), (5.2, 2.8), (6.0, 4.0), (6.3, 3.6), (9.7, 6.3), (10.0, 4.9), (11.0, 3.6), (12.5, 6.4) ] )
np.cov( points )
// 上面这个不对,形状都完全不对,然后看了一下,好像应该这么写:

>>> np.cov( points, rowvar = False )
array([[10.09288889,  3.73888889],
       [ 3.73888889,  2.24      ]])

这下形状对了,但是结果还是不一样啊,这个时候机智的我想到了在计算方差等等等老是出现的一个问题,那就是到底除以 N 还是除以 N-1,所以我来试验一下:

np.cov( points, rowvar = False ) / 10 * 9
array([[9.0836, 3.365 ],
       [3.365 , 2.016 ]])

cool! 跟上面的 covarience matrix 一致了, 下一步来计算 covarience matrix 的 eigenvector。Hmmm,既然是计算 eigenvector,那么乘以一个常数是没有影响的,继续使用 numpy:

>>> np.linalg.eig( np.cov( points, rowvar = False ))
(array([11.58827588,  0.74461301]), array([[ 0.92849112, -0.37135459],
       [ 0.37135459,  0.92849112]]))

出了一堆结果,根据这篇NumPy: Eigenvalues & Eigenvectors:

在这里插入图片描述
来解读,所以知道 eigenvector 是:
在这里插入图片描述
yep,又跟文章对上了, 根据文章,现在要继续的是

Projecting onto these vectors allow us to determine the OBB’s center and half-extent lengths, giving us the following image
文章就直接给出了结果,

然后突然发现了这里: Create the Oriented Bounding-box (OBB) with Python and NumPy

原来上面的除以 N 还是除以 N-1 是可以用 bias = 1 这个参数来描述的,完整的代码此处有:cool!

import matplotlib.pyplot as plt
import numpy as np

a  = np.array([(3.7, 1.7), (4.1, 3.8), (4.7, 2.9), (5.2, 2.8), (6.0,4.0), (6.3, 3.6), (9.7, 6.3), (10.0, 4.9), (11.0, 3.6), (12.5, 6.4)])
ca = np.cov(a,y = None,rowvar = 0,bias = 1)

v, vect = np.linalg.eig(ca)
tvect = np.transpose(vect)



fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(111)
ax.scatter(a[:,0],a[:,1])

#use the inverse of the eigenvectors as a rotation matrix and
#rotate the points so they align with the x and y axes
ar = np.dot(a,np.linalg.inv(tvect))

# get the minimum and maximum x and y 
mina = np.min(ar,axis=0)
maxa = np.max(ar,axis=0)
diff = (maxa - mina)*0.5

# the center is just half way between the min and max xy
center = mina + diff

#get the 4 corners by subtracting and adding half the bounding boxes height and width to the center
corners = np.array([center+[-diff[0],-diff[1]],center+[diff[0],-diff[1]],center+[diff[0],diff[1]],center+[-diff[0],diff[1]],center+[-diff[0],-diff[1]]])

#use the the eigenvectors as a rotation matrix and
#rotate the corners and the centerback
corners = np.dot(corners,tvect)
center = np.dot(center,tvect)

ax.scatter([center[0]],[center[1]])    
ax.plot(corners[:,0],corners[:,1],'-')

plt.axis('equal')
plt.show()

关键在下面的这几行代码:

tvect = np.transpose(vect)
ar = np.dot(a,np.linalg.inv(tvect))
mina = np.min(ar,axis=0)
maxa = np.max(ar,axis=0)
diff = (maxa - mina)*0.5
corners = np.array([center+[-diff[0],-diff[1]],center+[diff[0],-diff[1]],center+[diff[0],diff[1]],center+[-diff[0],diff[1]],center+[-diff[0],-diff[1]]])
corners = np.dot(corners,tvect)
center = np.dot(center,tvect)

其实上面的 transpose inverse, 变换 corner, 再变换回来,是不是可以这样理解,这一整个其实在做的就是坐标变换。我们由上图的 xy坐标变换到了 PCA 的轴的方向,然后再变换回来,我们才能得到正确的 center 和 corner.

2.2. OBB in 3D

看见以上的代码,我想直接代入3D points感觉应该也是 ok 的,唯一需要注意的是 3D 的 corners 维度更多,上面的代码基本上可以直接套用到 3D 的情况,令我惊讶的是我用 bunny 看到的结果居然是这样

在这里插入图片描述

我好惊讶,然后我看到 Fitting Oriented Bounding Boxes 这篇里的例子跟我一样,当用 points 来 fit OBB 的话就会这样
在这里插入图片描述

参考:Fitting Oriented Bounding Boxes
参考:Bounding Box - OOBB
参考:Create the Oriented Bounding-box (OBB) with Python and NumPy
参考:OBB generation via Principal Component Analysis

3. 函数实现

# 应用于2D、3D
def PCA(points):
    pointArray = np.array(points)
    ca = np.cov(pointArray,y = None,rowvar = 0,bias = 1)
    v, vect = np.linalg.eig(ca)
    tvect = np.transpose(vect)
    #use the inverse of the eigenvectors as a rotation matrix and
    #rotate the points so they align with the x and y axes
    ar = np.dot(pointArray,np.linalg.inv(tvect))
    
    # get the minimum and maximum x and y 
    mina = np.min(ar,axis=0)
    maxa = np.max(ar,axis=0)
    diff = (maxa - mina)*0.5
    # the center is just half way between the min and max xy
    center = mina + diff
    
    #get the 8 corners by subtracting and adding half the bounding boxes height and width to the center
    pointShape = pointArray.shape
    if pointShape[1] == 2:
        corners = np.array([center+[-diff[0],-diff[1]],
                        center+[diff[0],-diff[1]],
                        center+[diff[0],diff[1]],
                        center+[-diff[0],diff[1]],
                        center+[-diff[0],-diff[1]]])
    if pointShape[1] == 3:
        #get the 8 corners by subtracting and adding half the bounding boxes height and width to the center
        corners = np.array([center+[-diff[0],-diff[1],-diff[2]],
                    center+[diff[0],-diff[1],-diff[2]],                    
                    center+[diff[0],diff[1],-diff[2]],
                    center+[-diff[0],diff[1],-diff[2]],
                    center+[-diff[0],diff[1],diff[2]],
                    center+[diff[0],diff[1],diff[2]],                    
                    center+[diff[0],-diff[1],diff[2]],
                    center+[-diff[0],-diff[1],diff[2]],
                    center+[-diff[0],-diff[1],-diff[2]]])   
    
    #use the the eigenvectors as a rotation matrix and
    #rotate the corners and the centerback
    corners = np.dot(corners,tvect)
    center = np.dot(center,tvect)
    radius = diff
    if pointShape[1] == 2:
        array0,array1 = np.abs(vect[0,:]),np.abs(vect[1,:])
        index0,index1 = np.argmax(array0),np.argmax(array1)
        radius[index0],radius[index1] = diff[0],diff[1]
    if pointShape[1] == 3:
        array0,array1,array2 = np.abs(vect[0,:]),np.abs(vect[1,:]),np.abs(vect[2,:])
        index0,index1,index2 = np.argmax(array0),np.argmax(array1),np.argmax(array2)
        radius[index0],radius[index1],radius[index2] = diff[0],diff[1],diff[2]
    eigenvalue = v
    eigenvector = vect
    return corners, center, radius,eigenvalue,eigenvector

调用测试

corners, pcaCenter, pcaRadius,eigenvalue,eigenvector = PCA(rasPts)
print("corners", corners)
print("center", center)
print("radius", radius)
print("eigenvalue", eigenvalue)
print("eigenvector", eigenvector)

输出

corners [[-214.08716255 -287.34124873  329.15531199]
 [-206.02520693 -222.36772698  349.92398594]
 [-155.32541073 -228.84508711  350.5074316 ]
 [-163.38736634 -293.81860887  329.73875765]
 [-165.25498872 -305.17224576  365.98288493]
 [-157.1930331  -240.19872401  386.75155889]
 [-207.8928293  -233.72136387  386.16811323]
 [-215.95478492 -298.69488563  365.39943928]
 [-214.08716255 -287.34124873  329.15531199]]
center [-185.64009782 -263.76998637  357.95343544]
radius [25.55761109 34.34345453 19.01334956]
eigenvalue [221.80978801 109.57123123  54.06473478]
eigenvector [[ 0.11737252  0.99187275 -0.04911345]
 [ 0.94593748 -0.12672077 -0.29857014]
 [ 0.30236728  0.01141432  0.95312315]]

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

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

相关文章

最全java面试题及答案(208道)

本文分为十九个模块,分别是:「Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM」 ,如下图所…

Discord怎么抢白名单?Discord多账号防关联可以提高白名单成功率

Discord作为一个社交媒体,在加密货币领域扮演着重要的角色,它强大而便捷。更重要的是,如果你是一个投资者,Discord无疑是你了解项目动态,深入社区沟通交流的重要工具。 相信很多玩NFT和Web3的朋友都不会对discord 陌生…

分位数的理解与查询

目录 分位数的定义与理解: pyspark 中分位数查询 方式1 : 某列分位数查询: 采用dataframe的approxQuantile属性,返回一个list 例子: 方式2 : 转换为toPandas,利用pandas.dataframe利的…

全志T113-i+玄铁HiFi4开发板硬件说明书(2)

前 言 本文档主要介绍开发板硬件接口资源以及设计注意事项等内容,测试板卡为全志T113-i+玄铁HiFi4开发板,由于篇幅问题,本篇文章共分为上下两集,点击账户可查看更多内容详情,开发问题欢迎留言,感谢关注。 T113-i处理器的IO电平标准一般为1.8V和3.3V,上拉电源一般不超过…

【C++入门】命名空间的定义与使用

目  录1 命名空间1.1 命名空间定义1.2 命名空间使用1 命名空间 在C/C中,变量、函数和类都是大量存在的,这些变量、函数、类的名称将都存在于全局作用域中,可能会导致很多冲突。 使用命名空间的目的是对标识符的名称进行本地化,以…

经典动态规划OJ题目:接雨水or接青豆(多种方法,附详详细思维过程、解析及源码)

作者:非妃是公主 专栏:《算法》《刷题笔记》 个性签:顺境不惰,逆境不馁,以心制境,万事可成。——曾国藩 《算法》专栏系列文章 算法设计与分析复习01:主方法求递归算法时间复杂度 算法设计与分析…

MySQL批量插入4种方案(对比速度起飞)

一、前言二、准备工作1、Maven项目中pom.xml文件引入的相关依赖如下2、application.yml配置属性文件内容(重点:开启批处理模式)3、Entity实体类(测试)4、数据库student表结构(注意:无索引&#…

4.2 避免缩进错误

4.1.3 在for循环结束后执行一些操作for循环结束后再怎么做呢?通常,你需要提供总结性输出或接着执行程序必须完成的其他任务。在for循环后面,没有缩进的代码都只执行一次,而不会重复执行。下面来打印一条向全体魔术师致谢的消息,感谢他们的精…

爬虫实例(一) —— 5行 Python 代码爬取

大家好,我是 Enovo飞鱼,今天分享一个爬虫小案例,小白或者爬虫入门的小伙伴推荐阅读,加油💪。 目录 前言 基本环境配置 爬取目标网站 爬取内容 实现代码 后面我会继续更新爬虫实例,与大家共同学习&am…

ubuntu设置双网卡以及多网段

前提设备有两个网卡,分别作为内网和外网,比如两个网卡eth0和eth1,eth0作为外网,eth1作为内网,每个人的网卡名称可能不一样,网卡名称可以自行查看,使用ifconfig命令:ifconfig区分内网和外网的ip外网网卡&…

如何使用 Apache IoTDB 中的 UDF

本篇作者:IoTDB 社区 -- 廖兰宇本文将概述用户使用 UDF 的大致流程,UDF 的详细使用说明请参考官网用户手册:https://iotdb.apache.org/zh/UserGuide/Master/Operators-Functions/User-Defined-Function.html1编写 UDFIoTDB 为用户提供编写 UD…

opensuse15.4安装android-studio-2021.1.1.23_未完成

opensuse15.4安装android_studio再运行微信apk_未完成 未完成的原因是:本人台式机的物理内存为4G,官方要求是16G android studio与android sdkj是两个完全不同的两样东西。 Android studio是一个IDE(可视化开发工具)跟eclipse一…

SAP 新准则IFRS 17:重新思考和革新保险会计的机会

您准备好迎接 IFRS 17 了吗?财务报告准则过去曾多次更改,但这次没有。这一变化将为准备迎接新准则的保险公司带来广泛的机遇。许多公司都有应对这些要求的战术计划。但最大的成功将由制定包括 IFRS 17 在内的更广泛报告战略的公司实现。他们不会做出回应…

redis-哈希槽一致hash算法

1、一致性hash算法? 以分布式缓存为例,假设现在有3台缓存服务器(S0,S1,S2),要将一些图片尽可能平均地分配到不同的服务器上,hash算法的做法是: (1) 以图片的名称作为key,然后对其做…

【Unity3D插件】UniRx(基于Unity的响应式编程框架)插件教程

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客QQ群:1040082875 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、介绍UniRx插件 UniRx是一种基于Unity3D的…

九龙证券|石墨烯电池和锂离子电池哪个好

电池技术是电动汽车大力推广和开展的最大门槛,而电池工业正处于铅酸电池和传统锂电池开展均遇瓶颈的阶段,石墨烯储能设备的研制成功后,若能批量生产,则将为电池工业乃至电动车工业带来新的改造。那么石墨烯电池和锂离子电池哪个更…

VUE VS React 对比

VUE VS React 对比 这是面试中经常考察的一个问题,简单整理一下。我主要写 react ,所以 react 的特点阐述的多一点。 语法格式 vue 是单独的文件格式,一个文件包括了 js css HTML 全部 React 通常是 jsx 格式,JS 和 HTML 写在…

shiro相关源码解析

1. 认证过程相关源码解析 前后文可接查看 shiro的登陆都是通过subject.login()方法实现,接下来我们就进入login方法查看实现过程: 1.1 进入DelegatingSubject类的login方法: 此类实现了Subject接口: public void login(Authen…

基于ssm的高校二手物品交易网 java idea mysql

本文论述了民办高校二手物品交易网的设计和实现,该网站从实际运用的角度出发,运用了计算机网站设计、数据库等相关知识,网络和Mysql数据库设计来实现的,网站主要包括用户注册、用户登录、浏览商品、搜索商品、查看商品并进行购买&…

bilibili全链路压测改造之全链自动化测试实践

01 、背景与意义 B站直播营收送礼业务有着高写、在跨晚和S赛等大型活动下流量陡增、数据实时性要求高等特性,传统压测对于写场景为了避免影响线上数据做了各种屏蔽和黑名单处理,有着无法逼近线上真实情况的问题,因此业务对全链路压测有着较大…