2、PF-Net点云补全

2024-09-09 16:12
文章标签 net 点云 补全 pf

本文主要是介绍2、PF-Net点云补全,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2、PF-Net 点云补全

PF-Net论文链接:PF-Net

PF-NetPoint Fractal Network for 3D Point Cloud Completion)是一种专门为三维点云补全设计的深度学习模型。点云补全实际上和图片补全是一个逻辑,都是采用GAN模型的思想来进行补全,在图片补全中,将部分像素点删除并且标记,然后卷积特征提取预测、判别器判别,来训练模型,生成的像素点与原来像素点比较完成模型的训练。而PF-Net就是采用GAN的思想在3D点云上的应用。

模型架构

Multi-Resolution Encoder(MRE): 编码器,PF-Net在特征提取方面,在PointNet的基础上进行了改进,在不同层面进行采样,但是与PointNet++的多层面采样不一样,PF-Net没有像PointNet++一样提取某个点周围的点的特征,进行分组提取特征,而是采用最远距离采样,选取不同个个数的采样点,从细节到轮廓多层次采样,其次在进行卷积时,对于PointNet只在最后一层进行maxpooling来获得最终特征值,但是PF-Net则在提取的过程中,在卷积提取过程中,对卷积结果进行maxpooling提取特征,最终将多次maxpooling提取到的特征拼接,使特征既有轮廓信息,又有细节信息。

Point Pyramid Deconder(PPD): 解码器,在进行预测时,先是预测出一个轮廓,再将轮廓信息加入到下面,逐级预测更多的细节,慢慢加入特征,使预测更急准确。

Discriminator: 判別器,与GAN中的判别器逻辑一模一样,有两组输入,对比是否预测准确,实际上就是做了一个二分类,输出每个点结果为True或False,表示预测是否正确。

对于训练参数的更新,主要CD和判别器的Loss,CD计算的是每个预测点和距离他最近的正确点的距离,同时正确的点也会和预测点计算CD值,获得Loss值,更新参数。

模型架构图

在这里插入图片描述

数据准备
for i, data in enumerate(dataloader, 0): real_point, target = data  # 从dataloader中获取真实的点云数据和对应的目标标签batch_size = real_point.size()[0]  # 获取批量大小real_center = torch.FloatTensor(batch_size, 1, opt.crop_point_num, 3)  # 初始化用于存储被裁剪的点print(real_center.shape)  # 输出 real_center 的形状input_cropped1 = torch.FloatTensor(batch_size, opt.pnum, 3)  # 初始化裁剪后的输入点云数据print(input_cropped1.shape)  # 输出 input_cropped1 的形状 ([4, 2048, 3])# 将 real_point 复制到 input_cropped1 中input_cropped1 = input_cropped1.data.copy_(real_point)print(input_cropped1.shape)  # 输出复制后的 input_cropped1 的形状 ([4, 2048, 3])# 添加一个维度到 real_point,方便之后的操作real_point = torch.unsqueeze(real_point, 1)print(real_point.shape)  # 输出添加维度后的形状 ([4, 1, 2048, 3])# 添加一个维度到 input_cropped1,方便之后的操作input_cropped1 = torch.unsqueeze(input_cropped1,1)print(input_cropped1.shape)  # 输出添加维度后的 input_cropped1 的形状p_origin = [0,0,0]  # 设置原点位置# 根据不同的裁剪方法裁剪点云if opt.cropmethod == 'random_center':# 设置随机视角的选项choice = [torch.Tensor([1,0,0]), torch.Tensor([0,0,1]), torch.Tensor([1,0,1]), torch.Tensor([-1,0,0]), torch.Tensor([-1,1,0])]for m in range(batch_size):# 从选择中随机选取一个视角index = random.sample(choice,1)distance_list = []p_center = index[0]  # 选取的视角作为中心点for n in range(opt.pnum):# 计算每个点与中心点之间的距离distance_list.append(distance_squre(real_point[m,0,n], p_center))# 将距离排序,得到每个点的索引和值distance_order = sorted(enumerate(distance_list), key=lambda x: x[1])print(distance_order)  # 输出距离排序# 裁剪掉距离中心点最近的 opt.crop_point_num 个点for sp in range(opt.crop_point_num):input_cropped1.data[m, 0, distance_order[sp][0]] = torch.FloatTensor([0,0,0])  # 用 [0,0,0] 替换裁剪掉的点real_center.data[m, 0, sp] = real_point[m, 0, distance_order[sp][0]]  # 保存被裁剪的点到 real_center 中# 重新调整标签的大小并赋值label.resize_([batch_size, 1]).fill_(real_label)print(label.shape)  # 输出标签的形状# 将数据移到 GPU 设备上real_point = real_point.to(device)real_center = real_center.to(device)input_cropped1 = input_cropped1.to(device)label = label.to(device)############################# (1) 准备数据############################# 将 real_center 设置为可求导变量real_center = Variable(real_center, requires_grad=True)real_center = torch.squeeze(real_center, 1)  # 去除多余的维度# 通过最远点采样算法从 real_center 中选取64个关键点real_center_key1_idx = utils.farthest_point_sample(real_center, 64, RAN=False)real_center_key1 = utils.index_points(real_center, real_center_key1_idx)real_center_key1 = Variable(real_center_key1, requires_grad=True)  # 设置为可求导变量print(real_center_key1.shape)  # 输出关键点的形状# 通过随机最远点采样算法从 real_center 中选取128个关键点real_center_key2_idx = utils.farthest_point_sample(real_center, 128, RAN=True)real_center_key2 = utils.index_points(real_center, real_center_key2_idx)real_center_key2 = Variable(real_center_key2, requires_grad=True)  # 设置为可求导变量print(real_center_key2.shape)  # 输出关键点的形状# 去除 input_cropped1 的多余维度input_cropped1 = torch.squeeze(input_cropped1, 1)print(input_cropped1.shape)  # 输出去除维度后的形状# 从 input_cropped1 中采样不同数量的点input_cropped2_idx = utils.farthest_point_sample(input_cropped1, opt.point_scales_list[1], RAN=True)input_cropped2 = utils.index_points(input_cropped1, input_cropped2_idx)input_cropped3_idx = utils.farthest_point_sample(input_cropped1, opt.point_scales_list[2], RAN=False)input_cropped3 = utils.index_points(input_cropped1, input_cropped3_idx)# 将裁剪后的点云设置为可求导变量input_cropped1 = Variable(input_cropped1, requires_grad=True)print(input_cropped1.shape)  # 输出裁剪后的 input_cropped1 形状input_cropped2 = Variable(input_cropped2, requires_grad=True)print(input_cropped2.shape)  # 输出裁剪后的 input_cropped2 形状input_cropped3 = Variable(input_cropped3, requires_grad=True)print(input_cropped3.shape)  # 输出裁剪后的 input_cropped3 形状# 将裁剪后的数据移到 GPU 设备上input_cropped2 = input_cropped2.to(device)input_cropped3 = input_cropped3.to(device)# 将不同尺度的裁剪数据保存到列表中input_cropped = [input_cropped1, input_cropped2, input_cropped3]
Multi-Resolution Encoder(MRE)

**Multi-Resolution Encoder(MRE):**编码器,PF-Net在特征提取方面,在PointNet的基础上进行了改进,在不同层面进行采样,但是与PointNet++的多层面采样不一样,PF-Net没有像PointNet++一样提取某个点周围的点的特征,进行分组提取特征,而是采用最远距离采样,选取不同个个数的采样点,从细节到轮廓多层次采样,其次在进行卷积时,对于PointNet只在最后一层进行maxpooling来获得最终特征值,但是PF-Net则在提取的过程中,在卷积提取过程中,对卷积结果进行maxpooling提取特征,最终将多次maxpooling提取到的特征拼接,使特征既有轮廓信息,又有细节信息。

在这里插入图片描述

class Latentfeature(nn.Module):# ..def forward(self, x):outs = []  # 用于存储各层的输出结果print(x[0].shape)  # 打印输入数据第一个尺度的形状# 对第一个尺度的数据进行卷积处理,并将结果存入 outs 列表for i in range(self.each_scales_size):outs.append(self.Convlayers1[i](x[0]))# 对第二个尺度的数据进行卷积处理,并将结果存入 outs 列表for j in range(self.each_scales_size):outs.append(self.Convlayers2[j](x[1]))# 对第三个尺度的数据进行卷积处理,并将结果存入 outs 列表for k in range(self.each_scales_size):outs.append(self.Convlayers3[k](x[2]))# 将不同尺度的卷积输出在维度2上拼接起来,得到拼接后的特征latentfeature = torch.cat(outs, 2)# 将拼接后的特征进行转置操作,交换维度1和维度2latentfeature = latentfeature.transpose(1, 2)# 通过卷积层和批归一化层,对拼接特征进行卷积和激活处理latentfeature = F.relu(self.bn1(self.conv1(latentfeature)))# 压缩特征图的一个维度,将其从 (batch_size, 1, ...) 变为 (batch_size, ...)latentfeature = torch.squeeze(latentfeature, 1)return latentfeature  # 返回最终的特征图# 来源 self.Convlayers1[i](x[0])
class Convlayer(nn.Module):# ..def forward(self, x):x = torch.unsqueeze(x, 1)  # 在输入的维度上增加一个维度,转换为 (batch_size, 1, point_num, 3)# 通过第一层卷积+批归一化+ReLU激活x = F.relu(self.bn1(self.conv1(x)))# 通过第二层卷积+批归一化+ReLU激活x = F.relu(self.bn2(self.conv2(x)))# 通过第三层卷积,提取128通道的特征x_128 = F.relu(self.bn3(self.conv3(x)))# 通过第四层卷积,提取256通道的特征x_256 = F.relu(self.bn4(self.conv4(x_128)))# 通过第五层卷积,提取512通道的特征x_512 = F.relu(self.bn5(self.conv5(x_256)))# 通过第六层卷积,提取1024通道的特征x_1024 = F.relu(self.bn6(self.conv6(x_512)))# 使用最大池化层对 x_128 进行池化,去除掉特征图的某一维度x_128 = torch.squeeze(self.maxpool(x_128), 2)# 对其他特征图 x_256, x_512, x_1024 同样进行池化操作x_256 = torch.squeeze(self.maxpool(x_256), 2)x_512 = torch.squeeze(self.maxpool(x_512), 2)x_1024 = torch.squeeze(self.maxpool(x_1024), 2)# 将各层的特征图按通道拼接L = [x_1024, x_512, x_256, x_128]x = torch.cat(L, 1)  # 将四层特征拼接在一起return x  # 返回最终特征

这样的过程会进行三次,对于不同采样点个数都进行处理

Point Pyramid Deconder(PPD)

解码器,在进行预测时,先是预测出一个轮廓,再将轮廓信息加入到下面,逐级预测更多的细节,慢慢加入特征,使预测更急准确。

在这里插入图片描述

class _netG(nn.Module):def forward(self, x):# 获取输入特征x = self.latentfeature(x)# 通过全连接层并激活处理,逐步缩小特征维度x_1 = F.relu(self.fc1(x))  # 1024维度的特征处理x_2 = F.relu(self.fc2(x_1))  # 512维度的特征处理x_3 = F.relu(self.fc3(x_2))  # 256维度的特征处理# 第一阶段输出特征并 reshape 成 64x3 的中心点特征pc1_feat = self.fc3_1(x_3)pc1_xyz = pc1_feat.reshape(-1, 64, 3)  # 中心点1,形状为64x3# 第二阶段输出特征,并 reshape 为 128x64 的特征pc2_feat = F.relu(self.fc2_1(x_2))pc2_feat = pc2_feat.reshape(-1, 128, 64)  # 中心点2的特征pc2_xyz = self.conv2_1(pc2_feat)  # 处理中心点2# 第三阶段输出特征,并经过多个卷积层处理,最终 reshape 为 512x128 的特征pc3_feat = F.relu(self.fc1_1(x_1))pc3_feat = pc3_feat.reshape(-1, 512, 128)pc3_feat = F.relu(self.conv1_1(pc3_feat))pc3_feat = F.relu(self.conv1_2(pc3_feat))pc3_xyz = self.conv1_3(pc3_feat)  # 细化点云的特征# 扩展 pc1_xyz 的维度以便与 pc2_xyz 相加pc1_xyz_expand = torch.unsqueeze(pc1_xyz, 2)# 转置并 reshape pc2_xyz,然后与扩展后的 pc1_xyz 相加pc2_xyz = pc2_xyz.transpose(1, 2)pc2_xyz = pc2_xyz.reshape(-1, 64, 2, 3)pc2_xyz = pc1_xyz_expand + pc2_xyzpc2_xyz = pc2_xyz.reshape(-1, 128, 3)  # 中心点2的坐标# 扩展 pc2_xyz 的维度以便与 pc3_xyz 相加pc2_xyz_expand = torch.unsqueeze(pc2_xyz, 2)# 转置并 reshape pc3_xyz,然后与扩展后的 pc2_xyz 相加pc3_xyz = pc3_xyz.transpose(1, 2)pc3_xyz = pc3_xyz.reshape(-1, 128, int(self.crop_point_num / 128), 3)pc3_xyz = pc2_xyz_expand + pc3_xyzpc3_xyz = pc3_xyz.reshape(-1, self.crop_point_num, 3)  # 细化后的点云坐标# 返回第一阶段、第二阶段和最终阶段的点云坐标return pc1_xyz, pc2_xyz, pc3_xyz  # 返回中心点1、中心点2和细化点
Discriminator

判別器,与GAN中的判别器逻辑一模一样,有两组输入,对比是否预测准确,实际上就是做了一个二分类,输出每个点结果为True或False,表示预测是否正确。

class _netlocalD(nn.Module)# ...def forward(self, x):# 输入经过第一个卷积层和批归一化后激活x = F.relu(self.bn1(self.conv1(x)))# 输入经过第二个卷积层和批归一化后激活,得到 64 维的特征x_64 = F.relu(self.bn2(self.conv2(x)))# 输入经过第三个卷积层和批归一化后激活,得到 128 维的特征x_128 = F.relu(self.bn3(self.conv3(x_64)))# 输入经过第四个卷积层和批归一化后激活,得到 256 维的特征x_256 = F.relu(self.bn4(self.conv4(x_128)))# 对 64 维特征进行最大池化并 squeeze 以减少维度x_64 = torch.squeeze(self.maxpool(x_64))# 对 128 维特征进行最大池化并 squeeze 以减少维度x_128 = torch.squeeze(self.maxpool(x_128))# 对 256 维特征进行最大池化并 squeeze 以减少维度x_256 = torch.squeeze(self.maxpool(x_256))# 将 64、128 和 256 维的特征拼接在一起Layers = [x_256, x_128, x_64]x = torch.cat(Layers, 1)# 全连接层逐层缩小特征维度并进行激活x = F.relu(self.bn_1(self.fc1(x)))x = F.relu(self.bn_2(self.fc2(x)))x = F.relu(self.bn_3(self.fc3(x)))# 最后一层全连接层输出结果x = self.fc4(x)return x

这篇关于2、PF-Net点云补全的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1151652

相关文章

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

【LVI-SAM】激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节

激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节 1. 特征提取实现过程总结1.0 特征提取过程小结1.1 类 `FeatureExtraction` 的整体结构与作用1.2 详细特征提取的过程1. 平滑度计算(`calculateSmoothness()`)2. 标记遮挡点(`markOccludedPoints()`)3. 特征提取(`extractF

.NET 自定义过滤器 - ActionFilterAttribute

这个代码片段定义了一个自定义的 ASP.NET Core 过滤器(GuardModelStateAttribute),用于在控制器动作执行之前验证模型状态(ModelState)。如果模型状态无效,则构造一个 ProblemDetails 对象来描述错误,并返回一个 BadRequest 响应。 代码片段: /// <summary>/// 验证 ModelState 是否有效/// </

.Net Mvc-导出PDF-思路方案

效果图: 导语:     在我们做项目的过程中,经常会遇到一些服务性的需求,感到特别困扰,明明实用的价值不高,但是还是得实现;     因此小客在这里整理一下自己导出PDF的一些思路,供大家参考。     网上有很多导出PDF运用到的插件,大家也可以看看其他插件的使用,学习学习; 提要:     这里我使用的是-iTextSharp,供大家参考参考,借鉴方案,完善思路,补充自己,一起学习

.net MVC 导出Word--思路详解

序言:          一般在项目的开发过程中,总会接收到一个个需求,其中将数据转换成Work来下载,是一个很常见的需求;          那么,我们改如何处理这种需求,并输出实现呢?          在做的过程中,去思考 1、第一步:首先确认,Work的存在位置,并创建字符输出路:             //在的项目中创建一个存储work的文件夹             string

asp.net 中GridView的使用方法

可以看看,学习学习 https://blog.csdn.net/zou15093087438/article/details/79637042

点云数据常见的坐标系有哪些,如何进行转换?

文章目录 一、点云坐标系分类1. 世界坐标系2. 相机坐标系3. 极坐标系4. 笛卡尔坐标系(直角坐标系):5. 传感器坐标系6. 地理坐标系 二、坐标系转换方法1. 地理坐标系与投影坐标系之间的转换2. 投影坐标系与局部坐标系之间的转换3. 局部坐标系与3D模型坐标系之间的转换4. 相机坐标系与其他坐标系之间的转换5. 传感器坐标系与其他坐标系之间的转换 三、坐标系转换工具 一

三维激光扫描点云配准外业棋盘的布设与棋盘坐标测量

文章目录 一、棋盘标定板准备二、棋盘标定板布设三、棋盘标定板坐标测量 一、棋盘标定板准备 三维激光扫描棋盘是用来校准和校正激光扫描仪的重要工具,主要用于提高扫描精度。棋盘标定板通常具有以下特点: 高对比度图案:通常是黑白相间的棋盘格,便于识别。已知尺寸:每个格子的尺寸是已知的,可以用于计算比例和调整。平面标定:帮助校准相机和激光扫描仪之间的位置关系。 使用方法 扫描棋盘:

Linux下新手如何将VIM配置成C++编程环境(可以STL自动补全)

~ 弄拉老半天,终于弄的差不多啦,果然程序员还是需要有点折腾精神啊。 首先你要安装vim,命令:sudo apt-get install vim vim它只是一个编辑器,它不是IDE(比如codeblocks),IDE相当于已经给一个房子装好啦各种东西,你只要使用就行,vim却要自己装各种东西,相当于买了一个毛坯房,自己要给房子装潢。 如何安装g++编译器可以参考我上一篇博文. 1:vi