图神经网络:(处理点云)PPFNet的实现

news2024/12/29 11:28:36

文章说明:
1)参考资料:PYG官方文档。超链。
2)博主水平不高,如有错误还望批评指正。
3)我在百度网盘上传了这篇文章的jupyter notebook和有关文献。超链。提取码8848。

文章目录

    • 前言
    • 文献阅读
    • 代码实操
    • 历史遗留问题

前言

本篇文章接上一篇文章,超链,PointNet++无法解决旋转变化,所以我们使用PPFNet解决问题。

文献阅读

重要!重要!重要!我的理解不一定对!水平低请见谅!谨慎食用!
参考文献: PPFNet: Global Context Aware Local Features for Robust 3D Point Matching
文章概述: 参考文献提出Point Pair Feature NETwork模型,模型在点云中能够感知全局并且能够描述局部。模型受到PointNet的启发(PointNet++也是受到PointNet启发),采用新颖的N-tuple损失以及框架将全局信息嵌入于局部,具有置换不变性质,高召回率高鲁棒性。乃是3D点云提取特征重要工作文献。
理论概述:
11. 考虑两个点的集合 X X X以及 Y Y Y x i , y i x_{i},y_{i} xi,yi分别表示第 i i i个点坐标。假设它们存在某种关系并且是双射的,按照这篇The 3d-3d registration problem revisited的方法并且假定是刚性的。它们之间的关系可以分别表示为排列矩阵刚性变换。所以点集配准的L2可表示为: d ( X , Y ∣ R , t , P ) = 1 n ∑ i = 1 n ∣ ∣ x i − R y i ( P ) − t ∣ ∣ 2 d(X,Y|R,t,P)=\frac{1}{n}\sum_{i=1}^n||x_i-Ry_{i(P)}-t||^2 d(X,YR,t,P)=n1i=1n∣∣xiRyi(P)t2。符号说明: P P P指排列矩阵, T T T指刚性变换, T = { R ∈ S O ( 3 ) , t ∈ R 3 } T=\{{R\in SO(3)},t \in R^3\} T={RSO(3),tR3}。这里还是把L2理解为距离吧。
1). 排列矩阵是什么呢?如下一个矩阵便是一个44排列矩阵。表示集合{2,1,4,3}。第n行第m列为1,n在第m个的位置。
[ 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 ] \begin{bmatrix}0&1&0&0\\1&0&0&0\\0&0&0&1\\0&0&1&0\end{bmatrix} 0100100000010010
2). 刚性变换是什么呢?在几何学之中表示,使物体的大小形状不变变换。例如,平移,旋转,镜像。
12. 如果两个点集是数量相同的上述公式简化为 d ( X , Y ∣ T , P ) = 1 n ∣ ∣ X − P Y T T ∣ ∣ 2 d(X,Y|T,P)=\frac{1}{n}||X-PYT^\mathcal{T}||^2 d(X,YT,P)=n1∣∣XPYTT2。符号说明: T \mathcal{T} T是转置。如果两个点集匹配,那么有 d ( X , Y ∣ T , P ) ≈ 0 d(X,Y|T,P)\approx0 d(X,YT,P)0。得出结论:模型进行有效学习应该保持嵌入空间相似距离并且让 d ( X , Y ∣ T , P ) d(X,Y|T,P) d(X,YT,P)尽量为0。 d f ( X , Y ∣ T , P ) = 1 n ∣ ∣ f ( X ) − f ( P Y T T ) ∣ ∣ 2 d_f(X,Y|T,P)=\frac{1}{n}||f(X)-f(PYT^\mathcal{T})||^2 df(X,YT,P)=n1∣∣f(X)f(PYTT)2为保持不变性(即让它约为0), f f f应该对排列矩阵刚性变换极不敏感。但原文却说对刚性变换敏感。原文是“Ideally we would like to learn f being invariant to permutations P and as intolerant as possible to rigid transformations T”。还请大佬能够确认。
2. PPF stands for Point Pair Feature。我们对Point Pair Feature进行描述,它是一个4维的描述子。 ( ∥ p j − p i ∥ 2 , ∠ ( n i , p j − p i ) , ∠ ( n j , p j − p i ) , ∠ ( n i , n j ) ) (∥p_j−p_i∥^2,∠(n_i,p_j−p_i),∠(n_j,p_j−p_i),∠(n_i,n_j)) (pjpi2,(ni,pjpi),(nj,pjpi),(ni,nj))符号说明: P i P_i Pi是点集 X 1 X_1 X1 i i i个点的特征向量, P j P_j Pj是点集 X 2 X_2 X2 j j j个点的特征向量。 n n n指法线。 ∣ ∣ ⋅ ∣ ∣ ||\cdot|| ∣∣∣∣维欧几里得空间。 ∠ ( v 1 , v 2 ) = a t a n 2 ( ∣ ∣ v 1 × v 2 ∣ ∣ , v 1 ⋅ v 2 ) ∠(v_1,v_2) = atan2(||v_1\times v_2||,v_1 \cdot v_2) (v1,v2)=atan2(∣∣v1×v2∣∣,v1v2)
3. 这篇文章受到PointNet启发,鉴于篇幅假定你已经彻底搞懂了PointNet并且知道PointNet能够有效聚合全局信息但是同时难以捕捉局部信息。
流程概述:
1.局部区域点云编码。 先看看图因为图很好理解的。 F r F_r Fr为输入PPFNet的数据。
在这里插入图片描述
2.PPFNet的结构。 不难理解不再赘述。
在这里插入图片描述
3.N-tuple loss。 这是一个损失函数。如有兴趣建议自行阅读论文这一部分。博主没看因为我受不了。好了给我速速结束这一部分。

代码实操

导库

import torch
from torch.nn import Sequential,Linear,ReLU
from torch_geometric.nn import PPFConv
from torch_cluster import knn_graph
from torch_geometric.nn import global_max_pool

定义PPFNet框架
注意!注意!注意!注意!搭建PPFConv时输入维度必须加4,为什么呢?因为上面理论已经说了。输出维度还是不管你随意吧。

class PPFNet(torch.nn.Module):
    
    def __init__(self):
        super().__init__()
        mlp1=Sequential(Linear(4,20),ReLU(),Linear(20,24))
        self.conv1=PPFConv(mlp1)
        mlp2=Sequential(Linear(28,32),ReLU(),Linear(32,32))
        self.conv2=PPFConv(mlp2)
        self.classifier=Linear(32,40)
        
    def forward(self,pos,normal,batch):
        edge_index=knn_graph(pos,k=16,batch=batch,loop=False)
        x=self.conv1(x=None,pos=pos,normal=normal,edge_index=edge_index)
        x=x.relu()
        x=self.conv2(x=x,pos=pos,normal=normal,edge_index=edge_index)
        x=x.relu()
        x=global_max_pool(x,batch)
        return self.classifier(x)
    
model = PPFNet()
print(model)
#输出如下:
#PPFNet(
#  (conv1): PPFConv(local_nn=Sequential(
#    (0): Linear(in_features=4, out_features=20, bias=True)
#    (1): ReLU()
#    (2): Linear(in_features=20, out_features=24, bias=True)
#  ), global_nn=None)
#  (conv2): PPFConv(local_nn=Sequential(
#    (0): Linear(in_features=28, out_features=32, bias=True)
#    (1): ReLU()
#    (2): Linear(in_features=32, out_features=32, bias=True)
#  ), global_nn=None)
#  (classifier): Linear(in_features=32, out_features=40, bias=True)
#)

导库

from torch_geometric.transforms import Compose, RandomRotate,SamplePoints

定义旋转操作

random_rotate = Compose([
    RandomRotate(degrees=180, axis=0),
    RandomRotate(degrees=180, axis=1),
    RandomRotate(degrees=180, axis=2),
])
test_transform = Compose([
    random_rotate,
    SamplePoints(num=128, include_normals=True),
])

导库,导入数据数据变换,训测拆分打乱顺序

from torch_geometric.datasets import GeometricShapes
from torch_geometric.loader import DataLoader
train_dataset=GeometricShapes(root='/DATA/GeometricShapes',train=False,transform=SamplePoints(128,include_normals=True))
test_dataset=GeometricShapes(root='/DATA/GeometricShapes',train=False,transform=test_transform)
train_loader=DataLoader(train_dataset,batch_size=10,shuffle=True)
test_loader=DataLoader(test_dataset,batch_size=10)

jupyter nootbook内的输出如下
在这里插入图片描述

历史遗留问题

上篇文章我们使用K最邻近算法构边建图,好像这篇文章也是K最邻近算法构边建图。PointNet++那一篇文章指出这样不好(数据不均匀效果就不好,主观很好理解)。所以使用FPS算法来寻找质心克服问题。简单来说1.开始随便找个质心2.质心张开一个固定半径邻域3.再在剩下所有点内(质心以及质心邻域内的不算)寻找到离所有质心最远的点成为下个质心4迭代直到没有点了。
开摆

import matplotlib.pyplot as plt

def visualize_points(pos,edge_index=None,index=None):
    fig=plt.figure(figsize=(4, 4))
    if edge_index is not None:
        for (src,dst) in edge_index.t().tolist():
            src=pos[src].tolist()
            dst=pos[dst].tolist()
            plt.plot([src[0],dst[0]],[src[1],dst[1]],linewidth=1,color='black')
    if index is None:
        plt.scatter(pos[:,0],pos[:,1],s=50,zorder=1000)
    else:
       mask=torch.zeros(pos.size(0),dtype=torch.bool)
       mask[index]=True
       plt.scatter(pos[~mask,0],pos[~mask,1],s=50,color='lightgray',zorder=1000)
       plt.scatter(pos[mask,0],pos[mask,1],s=50,zorder=1000)
    plt.axis('off')
    plt.show()

from torch_cluster import fps

dataset=GeometricShapes(root='/DATA//GeometricShapes',transform=SamplePoints(128))
data=dataset[0]
index=fps(data.pos,ratio=0.25)
visualize_points(data.pos)
visualize_points(data.pos,index=index)

jupyter notebook内的输出如下
在这里插入图片描述
唯独这篇,点个赞吧,累麻

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

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

相关文章

【LeetCode: 410. 分割数组的最大值 | 暴力递归=>记忆化搜索=>动态规划 】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

这七种常见的路由协议,每一个网络工程师都应该知道!

你好,这里是网络技术联盟站。 路由协议是网络中非常重要的一个概念,它负责将数据包从源节点传递到目的节点。路由协议定义了网络中不同路由器之间的通信规则和数据传输方式,以便有效地将数据包从源地址传输到目标地址。在网络领域中&#xf…

基于无人机辅助边缘计算系统的节能卸载策略

源自:《系统工程与电子技术》 作者:余雪勇 朱烨 邱礼翔 朱洪波 摘 要 针对复杂地形中地面基础设施无法有效提供可靠通信和密集算力的问题,首先提出一种基于无人机(unmanned aerial vehicle, UAV)托管计算资源的卸载方案。考虑用户终端的计算需…

提升网站访问性的10个步骤优化

第一步,检查 不允许空,不允许过长,简洁明了。 是第一个可以访问到内容的元素,所以一定要非常重视。当用户切换浏览器Tab标签的时候,一定最先听到 标记的内容。Title一定要能代表当前页面的主题。这里的要求和SEO最佳实践几乎一致。 第二步,提供文字替代方案。 走查网页…

【LeetCode】238. 除自身以外数组的乘积

238. 除自身以外数组的乘积(中等) 方法一:左右乘积列表 思路 除了 nums[i] 以外各元素的积,就等同于 nums[i] 左边元素的乘积 * 右边元素的乘积,因此,我们可以计算出两个乘积列表 ,最后再经过一…

2023-05-23 题目

1、在java中定义一个无参的构造方法的作用: 在java程序执行子类的构造方法之前,如果是没有super()来调用父类的特定的构造方法,则会调用父类中没有参数的构造方法。 如果java中没有定义无参的方法,且没有指定super()方法&#x…

深眸科技探索AI机器视觉技术,助力密封圈缺陷识别检出率达99.8%

密封圈作为一种具有良好性能的密封材料,在工业领域具有极其广泛的应用,在汽车、船舶、管道、家用电器等多行业都能看见它的身影,广阔的市场需求促就密封圈产业的蓬勃发展。 密封圈属于大批量生产,在生产过程中难免会出现瑕疵品&a…

zabbix监控系统

一、Zabbix概述 1、使用zabbix的原因 作为一个运维,需要会使用监控系统查看服务器状态以及网站流量指标,利用监控系统的数据去了解上线发布的结果,和网站的健康状态。 利用一个优秀的监控软件,我们可以: ●通过一个友好的界面进…

vue 3.0使用 iframe 标签引入本地HTML页面,并实现数据交互

文章目录 1. 问题总结2. vue中引入html页面3. vue向html传递数据4. html向vue传递数据 1. 问题总结 最近在做vue的项目时候,需要引入本地html页面,中间遇到了很多问题,费时又费力,因此记录下来,以备不时之需&#xff…

顺序表 ArrayList

目录 1. 概念 2. ArrayList集合框架图 3.ArrayList常见的方法 4. 自己实现ArrayList(Integer) 4.1 ArrayList构造 4.2 ArrayList容量的扩容 4.3 判断空满 4.4 pos坐标是否合法(含有) 4.5 ArrayList的增删元素 4.6 包含元…

操作系统原理 —— 死锁的概念(十七)

什么是死锁 什么是死锁,如果你是个程序员,那么这概念肯定是不陌生的,死锁通常是指,在并发环境下,各个进程因竞争资源而造成一种相互等待的现象,导致的结果就是各个进程都处于阻塞状态,无法往下…

老司机解读香农定理、奈奎斯特定理、编码与调制

工程师都会考虑一个问题:信道上到底可以传输多大的数据,或者指定的信道上的极限传输率是多少。这就是信道容量的问题。例如,在xDSL系统中,我们使用的传输介质是仅有几兆带宽的电话线,而上面要传送几兆、十几兆甚至几十…

cuda编程学习——基础知识介绍!干货向(三)

本文主要内容为介绍CUDA编程前的一些基础知识 参考资料: 高升博客 《CUDA C编程权威指南》 以及 CUDA官方文档 文章、讲解视频同步更新公众《AI知识物语》,B站:出门吃三碗饭 1:并行计算 并行程序可以分为 指令并行&#xff1…

还在使用System.out+System.currentTimeMillis打印耗时?Xrebel是你不可或缺的神器!

1、概述 在Java应用程序中,性能是至关重要的。由于Java应用程序通常在高并发环境中运行,并处理大量数据,因此需要确保其能够高效地运行。为了帮助开发人员更好地实现Java应用程序的性能调优,ZeroTurnaround推出了XRebel。 XRebe…

测试用例的设计方法(全)

等价类划分方法 一.方法简介 1.定义 是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例。该方法是一种重要的,常用的黑盒测试用例设计方法。 2.划分等价类: 等价…

Stimulsoft 报表开发工具支持Laravel框架!一起来看~

Stimulsoft Reports 是一款报告编写器,主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署,如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等,在你的应用程序中嵌入报告设计器…

2023-05-24 LeetCode每日一题(T 秒后青蛙的位置)

2023-05-24每日一题 一、题目编号 1377. T 秒后青蛙的位置二、题目链接 点击跳转到题目位置 三、题目描述 给你一棵由 n 个顶点组成的无向树,顶点编号从 1 到 n。青蛙从 顶点 1 开始起跳。规则如下: 在一秒内,青蛙从它所在的当前顶点跳…

Jupyter Notebook 10个提升体验的高级技巧

Jupyter 笔记本是数据科学家和分析师用于交互式计算、数据可视化和协作的工具。Jupyter 笔记本的基本功能大家都已经很熟悉了,但还有一些鲜为人知的技巧可以大大提高生产力和效率。在这篇文章中,我将介绍10个可以提升体验的高级技巧。 改变注释的颜色 颜…

工程安全监测振弦采集仪在岩土工程中的应用

工程安全监测振弦采集仪在岩土工程中的应用 岩土工程中使用振弦采集仪在工程建设中起着至关重要的作用。振弦采集仪可以测量地面或者岩土中的振动参数,通过这些参数可以对地基、土壤和岩体的性质及其变化进行监测,帮助我们更好地了解工程地质条件和工程建…

Windows 11将加入Copilot的AI助手;约26%的中国用户已经部署了生成式AI技术

🚀 微软在Build开发者大会上宣布,将在Windows 11中加入一个名为Copilot的AI助手 微软在Build开发者大会上宣布,将在Windows 11中加入一个名为Copilot的AI助手,用户可以在任何应用程序中调用它,并根据用户的需求提供智…