pointnet语义分割_训练并预测自己的数据

本文介绍了使用PointNet进行点云分割的实验过程,包括数据预处理、模型训练和预测。作者通过CloudCompare软件进行点云标注,使用Python脚本进行随机变换、噪声添加和数据集生成。在训练过程中,调整PointNet网络结构以适应两类分割任务,并最终实现了高准确率的预测。
部署运行你感兴趣的模型镜像

这几天想用深度学习做一下点云的分割试验,网上搜了一下相关标题的blog有很多,但大部分只是简单的介绍文章内容,说明具体操作流程的干货并不多。在缺乏相关资料和帮助的情况下,本人大致搞清楚了pointnet进行sem_seg的流程。可能步骤存在问题甚至是错的,因为也没有人交流,但从试验结果看来还行。欢迎大家批评指正。点云的标注用cloudcompare(下面简称cc)就可以,后面obj文件的可视化由于cc对其支持不太好,改用meshlab。
欢迎阅读我的其他文章:
windows下运行pointnet(全)
pointnet C++推理部署(一)
pointnet C++推理部署(二)

训练

首先准备自己的数据集。txt格式的点云文件就行,我随便找了个斯坦福兔子点云文件,里面是xyz三维点的坐标。把兔子用cc软件分割成头(head)和身子(body),并分别保存成两个txt文件。
因为我比较懒所以只标注了这一个兔子,将其随机平移和旋转变换得到100个兔子的头和身子的txt,将其分别保存在编号为i(i=0,1,2,…,99)的文件夹下分别命名为head_i.txt和body_i.txt(i=0,1,2,…,99)。
修改sem_seg\meta\class_name.txt内容为head和body。
修改sem_seg\meta\anno_path.txt内容为训练标注的路径。
修改indoor3d_util.py中的g_class2color如下

g_class2color = {'head':	[255,0,0],
                 'body':	[0,0,255]}

修改collect_point_label函数里面的内容,给点云数据增加rgb信息:

   for f in glob.glob(os.path.join(anno_path, '*.txt')):
       print(f)
       cls = os.path.basename(f).split('_')[0]
       #print(cls)
       if cls not in g_classes: # note: in some room there is 'staris' class..
            cls = 'clutter'
       points = np.loadtxt(f)
       labels = np.ones((points.shape[0], 1)) * g_class2label[cls]
       color = np.zeros((points.shape[0], 3))
       color[:, 0:3] = 255
       points_list.append(np.concatenate([points, color, labels], 1))  # Nx7     

然后可以运行collect_indoor3d_data.py生成npy文件。再通过运行gen_indoor3d_h5.py生成h5文件,注意要修改sem_seg\meta\all_data_label.txt内容为上面生成的npy文件的路径。
得到h5文件后就可以进行训练了。train.py文件有几个地方要修改:NUM_CLASSES、train_idxs、test_idxs;还需要修改model.py文件中网络最后一个卷积层的通道数为要分类的数目。另外,输入参数num_point、max_epoch、batch_size可以根据自己情况修改。我的数据量较少,几分钟就能训练完毕。

预测

运行batch_inference.py程序即可。同理,需要修改NUM_CLASSES,以及修改sem_seg\meta\data_label.txt中的待预测模型的路径。运行需要提供的参数可以参考我的上一篇文章:windows下运行pointnet(全)
随便做了一些npy文件用于测试,制作过程和训练部分相似。用于预测的数据集包括随机平移旋转的模型,添加噪声的模型,裁剪的模型以及混合前三种的模型。红色为预测出的兔子头部,蓝色为预测出的兔子身体,黑色为未识别出的部分。因为我自始至终只是用了一个原始模型进行不同变换进行的测试,所以正确率都在99%以上。
随机平移旋转的模型预测结果:
在这里插入图片描述
添加噪声的模型预测结果:
在这里插入图片描述
裁剪的模型预测结果:在这里插入图片描述
混合平移旋转、添加噪声、裁剪的模型预测结果:在这里插入图片描述
整个工程文件打包:链接: https://pan.baidu.com/s/1yZG_OigNQqbeC852N6SMXw?pwd=zjn1 提取码: zjn1 复制这段内容后打开百度网盘手机App,操作更方便哦
–来自百度网盘超级会员v4的分享

本文参考的文章如下:
PointNet学习+训练自己的模型并实际使用测试成功

附预处理程序(Python实现和C++实现):
随机位姿变换

import numpy as np
import random

#文件名
old_file=r"rabbit.txt"
new_file=r"rabbit_change.txt"

#偏移参数
x_offset=random.uniform(-10, 10)
y_offset=random.uniform(-10, 10)
z_offset=random.uniform(-10, 10)

#缩放参数
scale=1.0

#旋转角度
roate_x=random.uniform(-np.pi/10, np.pi/10)
roate_y=random.uniform(-np.pi/10, np.pi/10)
roate_z=random.uniform(-np.pi/10, np.pi/10)

roate_x_matrix=np.array([
                    [1,0,0,0],
                    [0,np.cos(roate_x),-np.sin(roate_x),0],
                    [0,np.sin(roate_x),np.cos(roate_x),0],
                    [0,0,0,1]
                    ])
roate_y_matrix=np.array([
                    [np.cos(roate_y),0,np.sin(roate_y),0],
                    [0,1,0,0],
                    [-np.sin(roate_y),0,np.cos(roate_y),0],
                    [0,0,0,1]
                    ])
roate_z_matrix=np.array([
                    [np.cos(roate_z),-np.sin(roate_z),0,0],
                    [np.sin(roate_z),np.cos(roate_z),0,0],
                    [0,0,1,0],
                    [0,0,0,1]
                    ])

#变换矩阵
transformation_matrix=np.array([
                            [scale,0,0,x_offset],
                            [0,scale,0,y_offset],
                            [0,0,scale,z_offset],
                            [0,0,0,1]
                            ]).dot(roate_z_matrix).dot(roate_y_matrix).dot(roate_x_matrix)


#加载文件
old_array=np.loadtxt(old_file)
old_xyz=old_array[:,:3]

#补充数据为齐次项
ones_data=np.ones(old_xyz.shape[0])
old_xyz=np.insert(old_xyz,3,values=ones_data,axis=1)

#变换数据
new_xyz = np.dot(transformation_matrix,old_xyz.T)
new_array=np.concatenate((new_xyz.T[:,:3],old_array[:,3:]),axis=1)
np.savetxt(new_file,new_array,fmt='%.06f')
Eigen::Matrix4f random_transformation()
{
	//按三个欧拉角随机旋转:yaw和droll的范围是(- M_PI / 10, M_PI / 10),pitching的范围是(- M_PI / 20, M_PI / 20)
	float yaw = rand() * 2 /10.0 * M_PI / RAND_MAX - M_PI / 10.0;
	float pitching = rand() * M_PI / 10.0 / RAND_MAX - M_PI / 20.0;
	float droll = rand() * 2 * M_PI / 10.0 / RAND_MAX - M_PI / 10.0;
	
	Eigen::Vector3f ea0(yaw, pitching, droll);
	Eigen::Matrix3f R;
	R = Eigen::AngleAxisf(ea0[0], Eigen::Vector3f::UnitZ())*
		Eigen::AngleAxisf(ea0[1], Eigen::Vector3f::UnitY()) *
		Eigen::AngleAxisf(ea0[2], Eigen::Vector3f::UnitX());

	//按三坐标值平移,x、y、z平移范围均为(- 10, 10)
	float x = rand() * 20.0 / RAND_MAX - 10.0;
	float y = rand() * 20.0 / RAND_MAX - 10.0;
	float z = rand() * 20.0 / RAND_MAX - 10.0;

	Eigen::Vector3f T(x, y, z);
	Eigen::Matrix4f H;
	H << R, T, 0, 0, 0, 1;
	return H;
}

添加高斯噪声

import numpy as np

#文件名
old_file=r"rabbit.txt"
new_file=r"rabbit_change.txt"

def add_noise(point, sigma=0.1, clip=0.1):
    point = point.reshape(-1,3)
    Row, Col = point.shape
    noisy_point = np.clip(sigma * np.random.randn(Row, Col), -1*clip, clip)
    noisy_point += point
    return noisy_point
  
  
#加载文件
old_array=np.loadtxt(old_file)
old_xyz=old_array[:,:3]

new_xyz=add_noise(old_xyz)
new_array=np.concatenate((new_xyz,old_array[:,3:]),axis=1)
np.savetxt(new_file,new_array,fmt='%.06f')
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <boost/random.hpp>

int main(int argc, char** argv)
{
	boost::mt19937 rng;
	rng.seed(static_cast<unsigned int>(time(0)));
	boost::normal_distribution<> nd(0, 0.01);
	boost::variate_generator<boost::mt19937&, boost::normal_distribution<>> var_nor(rng, nd);

	std::ifstream infile;
	std::fstream outfile;
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloudfiltered(new pcl::PointCloud<pcl::PointXYZ>());
	float x, y, z;

	infile.open("17/head_17.txt");
	outfile.open("19/head_19.txt", 'w');
	while (infile >> x >> y >> z)
	{
		x += static_cast<float> (var_nor());
		y += static_cast<float> (var_nor());
		z += static_cast<float> (var_nor());
		outfile << x << " " << y << " " << z << std::endl;
	}
	infile.close();
	outfile.close();

	infile.open("17/body_17.txt");
	outfile.open("19/body_19.txt", 'w');
	while (infile >> x >> y >> z)
	{
		x += static_cast<float> (var_nor());
		y += static_cast<float> (var_nor());
		z += static_cast<float> (var_nor());
		outfile << x << " " << y << " " << z << std::endl;
	}
	infile.close();
	outfile.close();

	return EXIT_SUCCESS;
}

常见报错更新

  1. File “h5py_objects.pyx”, line 54, in h5py._objects.with_phil.wrapper
    File “h5py_objects.pyx”, line 55, in h5py._objects.with_phil.wrapper
    File “h5py\h5f.pyx”, line 106, in h5py.h5f.open
    FileNotFoundError: [Errno 2] Unable to open file (unable to open file: name = ‘E:\ruanjianchengxuwenjian\py\pointnet-rabbit\sem_seg/data\ply_data_all_0.h5’, errno = 2, error message = ‘No such file or directory’, flags = 0, o_flags = 0)
    报错原因:h5py包的版本过高
    解决方法:降低h5py包的版本,例如pip install h5py==2.10

  2. Exception has occurred: ValueError
    need at least one array to concatenate
    位于gen_indoor3d_h5.py的return np.concatenate(block_data_list, 0), np.concatenate(block_label_list, 0)
    报错原因:网格划分尺度过小,导致某个网格中点的个数为0
    解决方法:将

    data, label = indoor3d_util.room2blocks_wrapper_normalized(data_label_filename, NUM_POINT, block_size=1, stride=0.5,
                                                 random_sample=False, sample_num=None)

中的block_size和stride根据自己数据尺度进行调整,参数的含义见下图(从indoor3d_util.py中摘选)。
在这里插入图片描述

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>嗯,用户想了解如何使用PointNet在自定义数据集上进行语义分割训练,需要用到PyTorch或TensorFlow。首先,我应该回忆一下PointNet的基本结构。PointNet是处理点云数据的经典网络,用于分类、分割等任务。语义分割部分通常是在网络的后面添加分割头,输出每个点的类别。 接下来,用户需要的是自定义数据集的训练流程。可能需要从数据准备开始,包括数据格式、预处理、数据增强等。PointNet的输入是点云数据,通常是xyz坐标,可能还有其他特征如颜色、法向量等。自定义数据集需要转换成适合的格式,比如.npy或.tfrecords,或者直接使用PyTorch的Dataset类。 然后是模型搭建部分。PyTorch和TensorFlow的实现可能有不同。PyTorch的话,可以参考GitHub上的开源实现,定义PointNet的模型结构,包括T-Net(变换网络)、最大池化等层。语义分割的网络结构应该是在全局特征的基础上,将全局特征和局部特征拼接,再通过全连接层输出每个点的类别概率。 训练流程方面,需要确定损失函数,通常是交叉熵损失,可能还要考虑权重平衡。优化器的话,Adam比较常用。数据加载部分需要自定义Dataset类,读取点云和标签,可能还需要数据增强,比如随机旋转、抖动点坐标等。 验证和测试环节,需要计算指标如mIoU,或者点级别的准确率。另外,训练过程中可能要注意过拟合,使用Dropout层或者正则化方法。 用户提供的引用中,引用[1]提到了用Python跑模型写GUI,可能和训练关系不大。引用[2]关于卷积层的作用,但PointNet主要用全连接层和最大池化,可能不适用。引用[3]提到共享卷积的概念,但PointNet的结构不同,所以可能不需要考虑这个。 可能还需要考虑数据集的划分,训练集、验证集、测试集的比例。数据预处理是否包括归一化,比如将点云缩放到单位球体内。另外,点云的数量是否统一,比如采样到固定点数,如1024个点。 在PyTorch中,自定义数据集需要继承Dataset类,实现__len__和__getitem__方法。数据增强可以在__getitem__中应用,比如随机旋转、平移、添加噪声等。数据加载使用DataLoader,设置batch_size和shuffle等参数。 模型训练时,可能需要调整学习率,使用学习率调度器。训练循环中,每个epoch遍历DataLoader,前向传播,计算损失,反向传播,优化参数。 验证阶段,在每个epoch结束后,用验证集评估模型性能,保存最佳模型。测试阶段,用测试集评估最终效果。 此外,还要注意点云的输入格式,是否需要颜色或其他特征。标签数据应该是每个点的类别标签,格式可能是与点云对应的数组。 可能出现的问题包括显存不足,需要调整batch_size或点云的数量。或者数据不平衡,某些类别点较少,需要在损失函数中加权。 总结步骤应该是:数据准备→模型搭建→训练配置→训练循环→验证测试→结果可视化。 需要确保代码示例正确,比如PyTorch的模型定义、数据加载、训练循环等部分。还要提到可能的优化方法,如学习率调整、数据增强、模型保存和加载等。 最后,生成相关问题时要围绕数据准备、模型结构、训练技巧、评估指标等方向提问。</think>### PointNet自定义数据语义分割训练指南 #### 一、数据准备 1. **数据集格式要求** - 输入数据为点云数据,每个样本包含$N$个点,每个点至少包含三维坐标$(x,y,z)$,可扩展包含颜色、法向量等特征 - 标签数据为每个点的语义类别标签(整数型),形状为$(N,)$ - 推荐存储格式:`.npy`或`.h5`文件,支持批量加载 2. **数据预处理示例(PyTorch)** ```python import numpy as np import torch from torch.utils.data import Dataset class PointCloudDataset(Dataset): def __init__(self, data_path, n_points=1024): self.data = np.load(data_path) # 形状:(样本数, 点数, 特征数) self.labels = np.load(label_path) # 形状:(样本数, 点数) self.n_points = n_points def __getitem__(self, index): # 随机采样固定点数 pts = self.data[index] label = self.labels[index] if pts.shape[0] > self.n_points: idx = np.random.choice(pts.shape[0], self.n_points, replace=False) pts = pts[idx] label = label[idx] # 数据增强 pts += np.random.normal(0, 0.02, size=pts.shape) # 添加噪声 return torch.FloatTensor(pts), torch.LongTensor(label) ``` #### 二、模型搭建(PyTorch实现) ```python import torch.nn as nn import torch.nn.functional as F class PointNetSeg(nn.Module): def __init__(self, num_classes): super().__init__() # 共享特征提取层 self.conv1 = nn.Conv1d(3, 64, 1) self.conv2 = nn.Conv1d(64, 128, 1) self.conv3 = nn.Conv1d(128, 1024, 1) # 分割头 self.seg_conv = nn.Sequential( nn.Conv1d(1088, 512, 1), nn.Conv1d(512, 256, 1), nn.Conv1d(256, num_classes, 1) ) # T-Net self.transform = TNet(k=3) def forward(self, x): batch_size = x.size(0) n_pts = x.size(2) # 空间变换 trans = self.transform(x) x = x.transpose(2, 1) x = torch.bmm(x, trans) x = x.transpose(2, 1) # 特征提取 local_feat = F.relu(self.conv1(x)) point_feat = local_feat local_feat = F.relu(self.conv2(local_feat)) local_feat = self.conv3(local_feat) global_feat = torch.max(local_feat, 2, keepdim=True)[0] # 特征拼接 feat = torch.cat([point_feat, global_feat.repeat(1,1,n_pts)], 1) # 分割预测 return self.seg_conv(feat) ``` #### 三、训练流程 1. **训练配置** ```python import torch.optim as optim model = PointNetSeg(num_classes=5) optimizer = optim.Adam(model.parameters(), lr=0.001) criterion = nn.CrossEntropyLoss(weight=torch.tensor([1.0, 2.0, 1.5, 3.0, 1.0])) # 类别权重 ``` 2. **训练循环核心代码** ```python for epoch in range(100): model.train() for points, labels in train_loader: points = points.transpose(2, 1) # (B, 3, N) optimizer.zero_grad() outputs = model(points) # (B, num_classes, N) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 验证阶段 model.eval() with torch.no_grad(): total_correct = 0 for points, labels in val_loader: outputs = model(points) preds = outputs.argmax(dim=1) total_correct += (preds == labels).sum().item() accuracy = total_correct / len(val_dataset) ``` #### 四、关键注意事项 1. **数据增强策略** - 随机旋转:绕Z轴旋转$\theta \in [0,2\pi]$ - 随机缩放:缩放比例$s \in [0.8,1.2]$ - 添加噪声:$\epsilon \sim \mathcal{N}(0,0.02)$ - 随机丢弃点:丢弃率$p \in [0,0.2]$ 2. **评估指标** - 常用交比(IoU):$$IoU_c = \frac{TP_c}{TP_c+FP_c+FN_c}$$ - 平均IoU(mIoU):$$mIoU = \frac{1}{C}\sum_{c=1}^C IoU_c$$ - 点级别准确率:$$Acc = \frac{\sum_{i=1}^N \mathbb{I}(y_i = \hat{y}_i)}{N}$$ #### 五、常见优化方法 1. 使用**Focal Loss**解决类别不平衡: $$FL(p_t) = -\alpha_t(1-p_t)^\gamma \log(p_t)$$ 2. 添加**正则化项**: $$L_{total} = L_{CE} + \lambda\|\theta\|^2_2$$ 3. 采用**学习率衰减策略**: $$\eta_t = \eta_{initial} \times 0.95^{\lfloor t/10 \rfloor}$$ [^1]: 关于模型训练的基础框架选择可参考深度学习框架的标准实践方法 [^2]: 特征提取层的设计借鉴了卷积神经网络的核心思想
评论 131
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值