使用Python,Open3D对点云散点投影到面上并可视化,使用3种方法计算面的法向量及与平均法向量的夹角
创始人
2024-01-28 19:21:27
0

使用Python,Open3D对点云散点投影到面上并可视化,使用3种方法计算面的法向量及与平均法向量的夹角

写这篇博客源于博友的提问,他坚定了我继续坚持学习的心,带给了我充实与快乐。
将介绍以下5部分:

  1. 随机生成点云点
  2. 投影点到面(给出了6个面的中心点,离哪个中心点距离近就投影到哪个面)
  3. 对投影到每个面的点云计算法向量点(3种方法 KNN 半径近邻 混合近邻)
  4. 对每个面上的法向量及与平均法向量的夹角
  5. 可视化原始点及法向量点

1. 效果图

1.1 点云点灰色 VS 法向量点绿色 VS 法向量可视化

全量点云点灰色 VS 全量法向量点绿色效果图如下:
投影到每个面的均值点为渲染为红色比较明显。法向量点均值点渲染为黄色这里不太明显,可查看下边每个投影面的效果图;
在这里插入图片描述
全量点云点灰色 VS 全量法向量点绿色 VS 法向量可视化 效果图如下:

在这里插入图片描述

1.2 投影到每个面上的点云点灰色 VS 法向量点绿色 VS 均值点红色,法向量均值点黄色

投影到第一个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

红色,黄色点不明显,旋转下看下一个图;
在这里插入图片描述
投影到第一个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,旋转后红色、黄色点比较明显 如下图

在这里插入图片描述投影到第一个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时可视化法向量点 如下图

在这里插入图片描述

投影到第2个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

在这里插入图片描述

投影到第2个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时渲染法向量 如下图

在这里插入图片描述

投影到第3个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

在这里插入图片描述
投影到第3个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时渲染法向量 如下图

在这里插入图片描述

投影到第4个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

在这里插入图片描述
投影到第4个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时渲染法向量 如下图

在这里插入图片描述

投影到第5个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

在这里插入图片描述
投影到第5个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时渲染法向量 如下图

在这里插入图片描述

投影到第6个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,如下图

在这里插入图片描述
投影到第6个面上的原始点渲染为灰色,均值点渲染为红色;
法向量点渲染为绿色,法向量均值点渲染为黄色,同时渲染法向量 如下图

在这里插入图片描述

2. 源码

# 随机生成点
# 投影到面上,(给出了6个面的中心点,离哪个中心点距离近就投影到哪个面)
# 对投影到每个面的点云计算法向量点(3种方法 KNN 半径近邻 混合近邻)
# 对每个面上的法向量求均值及与平均法向量的夹角
# 并可视化原始点灰色,法向量点绿色,均值点红色,均值法向量点黄色
import randomimport numpy as np
import open3d as o3d# 随机种子,以便复现结果
random.seed(123)# 假设立方体是2*2*2,立方体的质心是笛卡尔坐标的原点,立方体外接球的半径为sqrt(3)d = {}  # 存储面的中心点及投影到每个面上的点云点,key中心 values投影到该面的点云点def add_dict(dictionary, loc, centroid):# loc is the point's location on sphere,# centroid is the center(s) of cube's face(s) that is nearest to the locif tuple(loc) in dictionary.keys():dictionary[tuple(loc)].append(list(centroid))else:dictionary[tuple(loc)] = [list(centroid)]def point_generator(npoints, r):result = []# 0 < theta < 2*np.pi# 0 < phi < np.pifor i in range(npoints):theta = random.random() * 2 * np.piphi = random.random() * np.pix = r * np.cos(theta) * np.sin(phi)y = r * np.sin(theta) * np.sin(phi)z = r * np.cos(phi)result.append([x, y, z])return resultnpoints = 5000
r = np.sqrt(3)
centroids = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]]
points = point_generator(npoints, r)# print(points)# 计算俩个点的距离
def distance(p, q):if len(p) == len(q):result = 0for i in range(len(p)):result += (p[i] - q[i]) ** 2return result ** 0.5else:print('cannot calculate distance of points from different dimensions')# 寻找离点最近的面(中心点),并投影到对应的面上
def archive_nearest_pc_pairs(dictionary, centroids, points):for p in points:dist = []for q in centroids:dist.append(distance(p, q))nearest_centroid = centroids[dist.index(min(dist))]  # 寻找最近的立方体面中心的点距离add_dict(dictionary, nearest_centroid, p)archive_nearest_pc_pairs(d, centroids, points)
print(centroids)# 3种方法计算法向量
def compute_normals(pcd, flag=1):# 混合搜索  KNN搜索  半径搜索if (flag == 1):pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=20))  # 计算法线,搜索半径1cm,只考虑邻域内的20个点elif (flag == 2):pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamKNN(knn=3))  # 计算法线,只考虑邻域内的20个点else:pcd.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamRadius(radius=0.01))  # 计算法线,搜索半径1cm,只考虑邻域内的20个点# 计算俩个法向量的夹角可参考 http://mp.ofweek.com/it/a456714148137
# x为第一个法向量,y为第二个法向量
def cal_angle(x, y):# 分别计算两个向量的模:l_x = np.sqrt(x.dot(x))l_y = np.sqrt(y.dot(y))# print('向量的模=', l_x, l_y)# 计算两个向量的点积dian = x.dot(y)# print('向量的点积=', dian)# 计算夹角的cos值:cos_ = dian / (l_x * l_y)# print('夹角的cos值=', cos_)# 求得夹角(弧度制):angle_hu = np.arccos(cos_)# print('夹角(弧度制)=', angle_hu)# 转换为角度值:angle_d = angle_hu * 180 / np.pi# print('夹角=%f°' % angle_d)return angle_d# 初始化法向量dict,key中心点 values法向量点
normals_dict = {}
pcds = []
for i, (key, value) in enumerate(d.items()):# print(value)val = np.array(value)# 计算点云均值点,并插入到原点云点的第一个点avg_point = np.mean(val, axis=0)  # axis=0,计算每一列的均值origin_points = np.row_stack((avg_point, val))# 构造点云数据pcd = o3d.geometry.PointCloud()points = o3d.utility.Vector3dVector(origin_points)pcd.points = points# 计算法向量,可选择3种方法计算法向量,传值1/2/其他compute_normals(pcd, 2)normals = o3d.np.asarray(pcd.normals)# print('pcd-normals: ', normals)normals_dict[key] = normalsprint('第', str(i + 1), '个面, center:', key, len(pcd.normals), '个点')# print(np.sum(normals) / len(normals))# 均值点法向量点avg_normal = normals[0]# 遍历计算平均向量与点云向量的夹角(由于第一个点是均值点,所以去除)for j, (point, point_normal) in enumerate(zip(origin_points[1:], normals[1:])):# 计算均值点法向量 与 点云点法向量的夹角print('\t第 %s 个点, angle: %s' % (str(j + 1),cal_angle(np.array(avg_point) - np.array(avg_normal), np.array(point) - np.array(point_normal))))print("--------------------------------------------------------")# 可视化法向量点和原始点pcd.paint_uniform_color([0.5, 0.5, 0.5])  # 把原始点渲染为灰色pcd.colors[0] = [1, 0, 0]  # 原始点云均值点渲染为红色normal_point = o3d.utility.Vector3dVector(pcd.normals)normals = o3d.geometry.PointCloud()normals.points = normal_pointnormals.paint_uniform_color((0, 1, 0))  # 点云法向量的点都以绿色显示normals.colors[0] = [1, 1, 0]  # 均值点法向量点渲染为黄色o3d.visualization.draw_geometries([pcd, normals], "Open3D origin " + str(i + 1) + " VS normals points True",width=800,height=600, left=50,top=50,point_show_normal=True, mesh_show_wireframe=False,mesh_show_back_face=False)o3d.visualization.draw_geometries([pcd, normals], "Open3D origin " + str(i + 1) + " VS normals points False",width=800,height=600, left=50,top=50,point_show_normal=False, mesh_show_wireframe=False,mesh_show_back_face=False)# 加入list以便全量渲染pcds.append(pcd)pcds.append(normals)print(pcds[0], pcds[1], pcds[2], pcds[3], pcds[4], pcds[5],pcds[6], pcds[7], pcds[8], pcds[9], pcds[10], pcds[11],len(pcds))
# 同时可视化法向量
o3d.visualization.draw_geometries([pcds[0], pcds[1], pcds[2], pcds[3], pcds[4],pcds[5], pcds[6], pcds[7], pcds[8], pcds[9], pcds[10], pcds[11]], "Open3D originAll VS normals points True",width=800,height=600, left=50,top=50,point_show_normal=True, mesh_show_wireframe=False,mesh_show_back_face=False)# 不可视化法向量
o3d.visualization.draw_geometries([pcds[0], pcds[1], pcds[2], pcds[3], pcds[4],pcds[5], pcds[6], pcds[7], pcds[8], pcds[9],pcds[10], pcds[11]], "Open3D originAll VS normals points False",width=800,height=600, left=50,top=50,point_show_normal=False, mesh_show_wireframe=False,mesh_show_back_face=False)

参考

  • 计算俩个法向量的夹角可参考 http://mp.ofweek.com/it/a456714148137

相关内容

热门资讯

中小板和创业板哪个好,创业板资...   写作缘由      上周,一个朋友来公司谈话,长期投资创业板50指数或科创板50指数是不是一个比...
未来健康行业发展趋势,健康食品...   研究表明,虽然个人健康仍然是购买食品的主要驱动力,但由于环境原因,消费者正在转向购买有机食品,这...
学校申请请假回家自学怎么写,大...         管理者在工作中应该有所为、有所不为。      同时,小仙的部门里还有两个性格很强的...
淘宝衣服店铺名字大全 淘宝衣服...   家庭装修,有很多东西其实可以在网上购买。只要不需要量确切的尺寸,不怕敲、易碎、易退的东西都可以在...
创业父母不给一分钱,拿父母钱去...   创业者是很孤独的。      作为一个电商老板,我经常约一些朋友喝酒,因为大家都是孤独的!我为什...
创业失败经历,为什么第一次创业...   #为什么在农村创业这么难#      为什么在农村创业这么难?      我不知道有多少人在农村...
陕西创业示范园区,陕西农村创业...   9月2日,由黄牛铺镇力推、县工商局评选的黄牛铺镇长潭坝村凤县鑫鑫五味子产业园项目获得陕西省第六届...
黄轩对宋轶评价,黄轩宋轶《创业...   文/马清运      到目前为止,电视剧《创业时代》已经播出了9集,基本上所有的主要角色都出现了...
创业板半年报业绩预增公告时间,...         本文首发于同名公众号      每年的7、8月份是a股半年报的发布日,可能有利于刺激...
2人合作开店怎么分钱,创业公司...   关注@ meme创业,分享金融投资,挖掘创业陷阱,助你登上人生巅峰!      当当。com的一...