点云分割体积计算方法(方法二)

方法一具体可见:点云分割体积计算方法(方法一)-CSDN博客

这里就不多赘述,直接介绍方法二,主要使用到是的pointnet点云分割的模型,具体分为以下几步:

1.数据集的制作(ColudCompare)

在这里我使用到的点云标注工具是CloudCompare,大家可以直接去官网进行下载,具体链接如下:CloudCompare - Open Source project

在下载好以后,我们打开会出现这样的界面:

打开之后,我们点击左上角的file,从里面导入我们的点云文件,

点击apply,就可以将我们的点云文件成功地导入到CloudCompare中了,在这里我以拍摄到的穿山甲点云图像作为示例,如下图所示:

由于我们需要做的是分割任务,于是我们需要将点云图中的穿山甲单独标记出来,如下图所示:

第一步,我们先点击点云图主体,会看到它出现一个黄色的立体框,接下来我们点击上方的那个小剪刀图案,来进行我们的分割,如下图所示:

这就是我们分割完成的点云图了,接下来,我们点击右上方的那个小五边形,就可以把我们标记好的物体单独分割出来了,之后再点击确定就好了

之后,我们再点击右上角那个蓝色的加号,给我们分割出来的点云图像添加标签,如下图所示:

输入你的标签名字,再输入标签数字(输入1或者0或者其他数字都行,随你便),之后我们就可以看到分离出来的点云部分被渲染成了蓝色,

之后,我们将所选的两个点云进行合并,

点击左上角的图标,注意,这里弹出来的选择框要选择no,合并完成后就可以得到这样的图了

然后,我们点击保存按钮,将其保存为txt格式

打开我们刚保存的txt格式的文本,可以看到如下所示:

到这一步,我们的数据准备环节就搞定了,接下来我们去GitHub上下载pointnet的源码文件进行解压,链接如下:GitHub - yanx27/Pointnet_Pointnet2_pytorch: PointNet and PointNet++ implemented by pytorch (pure python) and on ModelNet, ShapeNet and S3DIS.

2.准备训练

在训练之前,我们需要将我们txt格式的文件转化为h5格式,具体的代码如下所示:

import numpy as np
import h5py

def load_txt_file(file_path):
    data = np.loadtxt(file_path)
    points = data[:, :3].astype(np.float32)  # 前三列是点的坐标
    labels = data[:, 3].astype(np.int64)     # 最后一列是标签
    return points, labels

def normalize_point_cloud(points):
    centroid = np.mean(points, axis=0)
    points = points - centroid
    max_distance = np.max(np.sqrt(np.sum(points**2, axis=1)))
    points = points / max_distance
    return points

def sample_points(points, labels, num_points):
    if len(points) > num_points:
        idx = np.random.choice(len(points), num_points, replace=False)
    else:
        idx = np.random.choice(len(points), num_points, replace=True)
    return points[idx], labels[idx]



def save_to_hdf5(file_path, data, labels):
    with h5py.File(file_path, 'w') as f:
        f.create_dataset('data', data=data)
        f.create_dataset('label', data=labels)

# 假设每个TXT文件表示一个点云数据
txt_file_path = ""  #填入你的点云数据路径
points, labels = load_txt_file(txt_file_path)
points = normalize_point_cloud(points)
points, labels = sample_points(points, labels, 1024)  # 采样1024个点

# 保存为HDF5格式
hdf5_file_path = "" #填入你的保存h5文件的路径
save_to_hdf5(hdf5_file_path, points[np.newaxis, ...], labels[np.newaxis, ...])

这是单个txt文件转h5文件的代码,如果需要文件夹中多个文件进行转化的话,可以稍微修改下代码,加个for循环,进行遍历。之后,我们将转化好的文件放入一个文件夹中。

之后,我们可以运行训练的代码,如下所示:

import os
import h5py
import numpy as np
import torch
from torch.utils.data import Dataset, DataLoader
import torch.nn as nn
import torch.optim as optim
from models.pointnet import PointNetSeg

class PointCloudDataset(Dataset):
    def __init__(self, folder_path, num_points=1024, num_classes=2):
        self.folder_path = folder_path
        self.num_points = num_points
        self.num_classes = num_classes
        self.files = [f for f in os.listdir(folder_path) if f.endswith('.h5')]
    
    def __len__(self):
        return len(self.files)
    
    def __getitem__(self, idx):
        file_path = os.path.join(self.folder_path, self.files[idx])
        with h5py.File(file_path, 'r') as f:
            data = f['data'][:]
            labels = f['label'][:]
        
        points, labels = self.sample_points(data[0], labels[0], self.num_points)
        points = self.normalize_point_cloud(points)
        
        # Ensure labels are within the expected range
        labels = np.clip(labels, 0, self.num_classes - 1)

        return torch.from_numpy(points).float(), torch.from_numpy(labels).long()
    
    def normalize_point_cloud(self, points):
        centroid = np.mean(points, axis=0)
        points = points - centroid
        max_distance = np.max(np.sqrt(np.sum(points**2, axis=1)))
        points = points / max_distance
        return points
    
    def sample_points(self, points, labels, num_points):
        if len(points) > num_points:
            idx = np.random.choice(len(points), num_points, replace=False)
        else:
            idx = np.random.choice(len(points), num_points, replace=True)
        return points[idx], labels[idx]

# 示例使用
folder_path = "" #输入你的h5文件所在的文件夹
num_classes = 2  # Adjust based on your dataset
dataset = PointCloudDataset(folder_path, num_classes=num_classes)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 设置设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device) #测试当前所用的是GPU还是CPU

# 构建PointNet模型
in_dim = 3  # 输入维度,点云通常为3 (x, y, z)
model = PointNetSeg(in_dim=in_dim, out_dim=num_classes).to(device)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
num_epochs = 100  #训练轮数
for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    for i, (points, labels) in enumerate(dataloader):
        points = points.transpose(1, 2).to(device)  # 转置输入形状为 [batch_size, in_channels, num_points]
        labels = labels.to(device)

        optimizer.zero_grad()
        outputs = model(points)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
    
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(dataloader):.4f}')

# 保存模型
torch.save(model.state_dict(), "") #输入你的模型保存地址

需要注意的是,我们要确保文件结构如下图所示:

不然在from models.pointnet import时会报错,之后我们就可以运行代码进行训练了,这是我跑了100轮的结果:

之后,我们可以用我们训练好的模型来进行预测并计算体积,预测代码如下所示:

import torch
import open3d as o3d
import numpy as np
from models.pointnet import PointNetSeg

# 设置设备
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(device)

# 加载训练好的模型
model_path = ""  # 请替换为你的模型路径
num_classes = 2  # 根据你的任务设置,
in_dim = 3  # 输入维度,点云通常为3 (x, y, z)
out_dim = num_classes  # 输出维度
model = PointNetSeg(in_dim=in_dim, out_dim=out_dim).to(device)
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()

# 读取点云数据
point_cloud_path = ""  # 请替换为你的点云文件路径
pcd = o3d.io.read_point_cloud(point_cloud_path)
points = np.asarray(pcd.points)

# 将点云数据转换为张量并调整形状
points_tensor = torch.tensor(points, dtype=torch.float32).unsqueeze(0).transpose(1, 2).to(device)

# 进行分割预测
with torch.no_grad():
    preds = model(points_tensor)

# 确认预测结果的形状
print(f"Shape of preds: {preds.shape}")

# 将预测结果转换为标签
preds = preds.squeeze().cpu().numpy()
pred_labels = np.argmax(preds, axis=0)

# 确认标签的形状
print(f"Shape of pred_labels: {pred_labels.shape}")
print(f"Shape of points: {points.shape}")

# 提取点云
pangolin_points = points[pred_labels == 1]  # 假设标签1表示你的点云

# 检查是否有你的分割点云
if len(pangolin_points) == 0:
    print("未检测到任何穿山甲点云。")
else:
    # 使用选中的点创建新的点云
    pangolin_pcd = o3d.geometry.PointCloud()
    pangolin_pcd.points = o3d.utility.Vector3dVector(pangolin_points)

    # 计算体积
    # 使用体素化方法
    voxel_size = 0.01  # 根据需要调整体素大小
    voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud(pangolin_pcd, voxel_size)
    voxel_volume = len(voxel_grid.get_voxels()) * (voxel_size ** 3)

    # 输出体积
    print(f"Estimated Volume of Pangolin: {voxel_volume} cubic units")

    # 可视化分割点云
    o3d.visualization.draw_geometries([pangolin_pcd])


至此,所有步骤就完成了,但是有几点值得注意:

1.训练和预测时对于显存要求比较大,可以先尝试运行看看你的设备是否可以运行

2.数据集的要求会比较大,如果训练的数据集较小,可能会没什么效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值