资讯详情

【项目分享】使用 PointNet 进行点云分割

介绍

“点云(point cloud)它是一种重要的数据结构类型,用于存储几何形状数据。由于其不规则的格式,它通常在深度学习应用程序之前转换为规则 3D 体素网格或图像集合会使数据变得不必要。PointNet 该系列模型通过直接使用点云来解决这个问题,并尊重点数据的不变性。PointNet 系列模型为从等待应用程序提供简单统一的架构。

在这个例子中,我们演示了形状分割的用途 PointNet 实现架构。

参考

  • PointNet:用于 3D 分类和分割点集的深度学习
  • 使用 PointNet 点云分类
  • 空间变压器网络

导入

import os import json import random import numpy as np import pandas as pd from tqdm import tqdm from glob import glob import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers import matplotlib.pyplot as plt 

下载数据集

ShapeNet建立注释丰富的大规模数据集 3D 形状数据集的持续努力。是完整 ShapeNet 数据集的子集单个清洁 3D 模型和手动验证的类别和对齐注释。它涵盖了 55 常见的对象类别大约有 51,300 个独特的 3D 模型。

我们用这个例子。PASCAL 3D 的 12 作为对象类别之一 ShapenetCore 数据集的一部分。

dataset_url = "https://git.io/JiY4i" dataset_path = keras.utils.get_file( fname="shapenet.zip", origin=dataset_url, cache_subdir="datasets", hash_algorithm="auto", extract=True, archive_format="auto", cache_dir="datasets", ) 

加载数据集

我们解析数据集元数据,以便轻松地将模型类别映射到它们各自的目录,并将分割类映射到颜色以实现可视化。

with open("/tmp/.keras/datasets/PartAnnotation/metadata.json") as json_file: metadata = json.load(json_file) print(metadata) 
{'Airplane': {'directory': '02691156', 'lables': ['wing', 'body', 'tail', 'engine'], 'colors': ['blue', 'green', 'red', 'pink']}, 'Bag': {'directory': '02773838', 'lables': ['handle', 'body'], 'colors': ['blue', 'green']}, 'Cap': {'directory': '02954340', 'lables': ['panels', 'peak'], 'colors': ['blue', 'green']}, 'Car': {'directory': '02958343', 'lables': ['wheel', 'hood', 'roof'], 'colors': ['blue', 'green', 'red']}, 'Chair': {'directory': '03001627', 'lables': ['leg', 'arm', 'back', 'seat'], 'colors': ['blue', 'green', 'red', 'pink']}, 'Earphone': {'directory': '03261776', 'lables': ['earphone', 'headband'], 'colors': ['blue', 'green']}, 'Guitar': {'directory': '03467517', 'lables': ['head', 'body', 'neck'], 'colors': ['blue', 'green', 'red']}, 'Knife': {'directory': '03624134', 'lables': ['handle', 'blade'], 'colors': ['blue', 'green']}, 'Lamp': {'directory': '03636649', 'lables': ['canopy', 'lampshade', 'base'], 'colors': ['blue', 'green', 'red']}, 'Laptop': {'directory': '03642806', 'lables': ['keyboard'], 'colors': ['blue']}, 'Motorbike': {'directory': '03790512', 'lables': ['wheel', 'handle', 'gas_tank', 'light', 'seat'], 'colors': ['blue', 'green', 'red', 'pink', 'yellow']}, 'Mug': {'directory': '03797390', 'lables': ['handle'], 'colors': ['blue']}, 'Pistol': {'directory': '03948459', 'lables': ['trigger_and_guard', 'handle', 'barrel'], 'colors': ['blue', 'green', 'red']}, 'Rocket': {'directory': '04099429', 'lables': ['nose', 'body', 'fin'], 'colors': ['blue', 'green', 'red']}, 'Skateboard': {'directory': '04225987', 'lables': ['wheel', 'deck'], 'colors': ['blue', 'green']}, 'Table': {'directory': '04379243', 'lables': ['leg', 'top'], 'colors': ['blue', 'green']}} 

在这个例子中,我们训练 PointNet 来分割Airplane模型的各个部分。

points_dir = "/tmp/.keras/datasets/PartAnnotation/{}/points".format( metadata["Airplane"]["directory"] ) labels_dir = "/tmp/.keras/datasets/PartAnnotation/{}/points_label".format( metadata["Airplane"]["directory"] ) LABELS = metadata["Airplane"]["lables"] COLORS = metadata["Airplane"]["colors"] VAL_SPLIT = 0.2 NUM_SAMPLE_POINTS = 1024 BATCH_SIZE = 32 EPOCHS = 60 INITIAL_LR = 1e-3 

构建数据集

我们从飞机点云及其标签生成以下内存数据结构:

  • point_cloudsnp.array是以x、y 和 z 坐标的形式表示点云数据的对象列表。轴 0 表示点云中的点数,轴 1 表示坐标。all_labels是将每个坐标的标签表示为字符串的列表(主要用于可视化目的)。
  • test_point_clouds与 格式相同point_clouds,但没有对应的点云标签。
  • all_labelsnp.array表示每个坐标的点云标签的对象列表,对应于point_clouds列表。
  • point_cloud_labels是一个np.array对象列表,它以 one-hot 编码形式表示每个坐标的点云标签,对应于point_clouds 列表。
point_clouds, test_point_clouds = [], [] point_cloud_labels, all_labels = [], [] points_files = glob(os.path.join(points_dir, "*.pts")) for point_file in tqdm(points_files): point_cloud = np.loadtxt(point_file) if point_cloud.shape[0] < NUM_SAMPLE_POINTS: continue # Get the file-id of the current point cloud for parsing its # labels. file_id = point_file.split("/")[-1].split(".")[0] label_data, num_labels = {}, 0 for label in LABELS: label_file = os.path.join(labels_dir, label, file_id + ".seg") if os.path.exists(label_file): label_data[label] = np.loadtxt(label_file).astype("float32") num_labels = len(label_data[label]) # Point clouds having labels will be our training samples. try: label_map = ["none"] * num_labels for label in LABELS: for i, data in enumerate(label_data[label]): label_map[i] = label if data == 1 else label_map[i] label_data = [ LABELS.index(label) if label != "none" else len(LABELS) for label in label_map ] # Apply one-hot encoding to the dense label representation. label_data = keras.utils.to_categorical(label_data, num_classes=len(LABELS) + 1) point_clouds.append(point_cloud) point_cloud_labels.append(label_data) all_labels.append(label_map) except KeyError: test_point_clouds.append(point_cloud) 
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4045/4045 [03:35<00:00, 18.76it/s] 

接下来,我们看一下刚刚生成的内存数组中的一些样本:

for _ in range(5): i = random.randint(0, len(point_clouds) - 1) print(f"point_clouds[{i}].shape:", point_clouds[0].shape) print(f"point_cloud_labels[{i}].shape:", point_cloud_labels[0].shape) for j in range(5): print( f"all_labels[{i}][{j}]:", all_labels[i][j], f"\tpoint_cloud_labels[{i}][{j}]:", point_cloud_labels[i][j], "\n", ) 
point_clouds[475].shape: (2602, 3) point_cloud_labels[475].shape: (2602, 5) all_labels[475][0]: body point_cloud_labels[475][0]: [0. 1. 0. 0. 0.] 
all_labels[475][1]: engine point_cloud_labels[475][1]: [0. 0. 0. 1. 0.] 
all_labels[475][2]: body point_cloud_labels[475][2]: [0. 1. 0. 0. 0.] 
all_labels[475][3]: body point_cloud_labels[475][3]: [0. 1. 0. 0. 0.] 
all_labels[475][4]: wing point_cloud_labels[475][4]: [1. 0. 0. 0. 0.] 
point_clouds[2712].shape: (2602, 3) point_cloud_labels[2712].shape: (2602, 5) all_labels[2712][0]: tail point_cloud_labels[2712][0]: [0. 0. 1. 0. 0.] 
all_labels[2712][1]: wing point_cloud_labels[2712][1]: [1. 0. 0. 0. 0.] 
all_labels[2712][2]: engine point_cloud_labels[2712][2]: [0. 0. 0. 1. 0.] 
all_labels[2712][3]: wing point_cloud_labels[2712][3]: [1. 0. 0. 0. 0.] 
all_labels[2712][4]: wing point_cloud_labels[2712][4]: [1. 0. 0. 0. 0.] 
point_clouds[1413].shape: (2602, 3) point_cloud_labels[1413].shape: (2602, 5) all_labels[1413][0]: body point_cloud_labels[1413][0]: [0. 1. 0. 0. 0.] 
all_labels[1413][1]: tail point_cloud_labels[1413][1]: [0. 0. 1. 0. 0.] 
all_labels[1413][2]: tail point_cloud_labels[1413][2]: [0. 0. 1. 0. 0.] 
all_labels[1413][3]: tail point_cloud_labels[1413][3]: [0. 0. 1. 0. 0.] 
all_labels[1413][4]: tail point_cloud_labels[1413][4]: [0. 0. 1. 0. 0.] 
point_clouds[1207].shape: (2602, 3) point_cloud_labels[1207].shape: (2602, 5) all_labels[1207][0]: tail point_cloud_labels[1207][0]: [0. 0. 1. 0. 0.] 
all_labels[1207][1]: wing point_cloud_labels[1207][1]: [1. 0. 0. 0. 0.] 
all_labels[1207][2]: wing point_cloud_labels[1207][2]: [1. 0. 0. 0. 0.] 
all_labels[1207][3]: body point_cloud_labels[1207][3]: [0. 1. 0. 0. 0.] 
all_labels[1207][4]: body point_cloud_labels[1207][4]: [0. 1. 0. 0. 0.] 
point_clouds[2492].shape: (2602, 3) point_cloud_labels[2492].shape: (2602, 5) all_labels[2492][0]: engine point_cloud_labels[2492][0]: [0. 0. 0. 1. 0.] 
all_labels[2492][1]: body point_cloud_labels[2492][1]: [0. 1. 0. 0. 0.] 
all_labels[2492][2]: body point_cloud_labels[2492][2]: [0. 1. 0. 0. 0.] 
all_labels[2492][3]: body point_cloud_labels[2492][3]: [0. 1. 0. 0. 0.] 
all_labels[2492][4]: engine point_cloud_labels[2492][4]: [0. 0. 0. 1. 0.] 

现在,让我们可视化一些点云及其标签。

def visualize_data(point_cloud, labels): df = pd.DataFrame( data={ 
            "x": point_cloud[:, 0], "y": point_cloud[:, 1], "z": point_cloud[:, 2], "label": labels, } ) fig = plt.figure(figsize=(15, 10)) ax = plt.axes(projection="3d") for index, label in enumerate(LABELS): c_df = df[df["label"] == label] try: ax.scatter( c_df["x"], c_df["y"], c_df["z"], label=label, alpha=0.5, c=COLORS[index] ) except IndexError: pass ax.legend() plt.show() visualize_data(point_clouds[0], all_labels[0]) visualize_data(point_clouds[300], all_labels[300]) 

预处理

请注意,我们加载的所有点云都包含可变数量的点,这使得我们很难将它们批处理在一起。为了克服这个问题,我们从每个点云中随机抽取固定数量的点。我们还对点云进行归一化,以使数据具有尺度不变性。

for index in tqdm(range(len(point_clouds))): current_point_cloud = point_clouds[index] current_label_cloud = point_cloud_labels[index] current_labels = all_labels[index] num_points = len(current_point_cloud) # Randomly sampling respective indices. sampled_indices = random.sample(list(range(num_points)), NUM_SAMPLE_POINTS) # Sampling points corresponding to sampled indices. sampled_point_cloud = np.array([current_point_cloud[i] for i in sampled_indices]) # Sampling corresponding one-hot encoded labels. sampled_label_cloud = np.array([current_label_cloud[i] for i in sampled_indices]) # Sampling corresponding labels for visualization. sampled_labels = np.array([current_labels[i] for i in sampled_indices]) # Normalizing sampled point cloud. norm_point_cloud = sampled_point_cloud - np.mean(sampled_point_cloud, axis=0) norm_point_cloud /= np.max(np.linalg.norm(norm_point_cloud, axis=1)) point_clouds[index] = norm_point_cloud point_cloud_labels[index] = sampled_label_cloud all_labels[index] = sampled_labels 
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3694/3694 [00:07<00:00, 478.67it/s] 

让我们可视化采样和归一化的点云及其相应的标签。

visualize_data(point_clouds[0], all_labels[0]) visualize_data(point_clouds[300], all_labels[300]) 

创建 TensorFlow 数据集

tf.data.Dataset我们为训练和验证数据创建对象。我们还通过对其应用随机抖动来增强训练点云。

def load_data(point_cloud_batch, label_cloud_batch): point_cloud_batch.set_shape([NUM_SAMPLE_POINTS, 3]) label_cloud_batch.set_shape([NUM_SAMPLE_POINTS, len(LABELS) + 1]) return point_cloud_batch, label_cloud_batch def augment(point_cloud_batch, label_cloud_batch): noise = tf.random.uniform( tf.shape(label_cloud_batch), -0.005, 0.005, dtype=tf.float64 ) point_cloud_batch += noise[:, :, :3] return point_cloud_batch, label_cloud_batch def generate_dataset(point_clouds, label_clouds, is_training=True): dataset = tf.data.Dataset.from_tensor_slices((point_clouds, label_clouds)) dataset = dataset.shuffle(BATCH_SIZE * 100) if is_training else dataset dataset = dataset.map(load_data, num_parallel_calls=tf.data.AUTOTUNE) dataset = dataset.batch(batch_size=BATCH_SIZE) dataset = ( dataset.map(augment, num_parallel_calls=tf.data.AUTOTUNE) if is_training else dataset ) return dataset split_index = int(len(point_clouds) * (1 - VAL_SPLIT)) train_point_clouds = point_clouds[:split_index] train_label_cloud = point_cloud_labels[:split_index] total_training_examples = len(train_point_clouds) val_point_clouds = point_clouds[split_index:] val_label_cloud = point_cloud_labels[split_index:] print("Num train point clouds:", len(train_point_clouds)) print("Num train point cloud labels:", len(train_label_cloud)) print("Num val point clouds:", len(val_point_clouds)) print("Num val point cloud labels:", len(val_label_cloud)) train_dataset = generate_dataset(train_point_clouds, train_label_cloud) val_dataset = generate_dataset(val_point_clouds, val_label_cloud, is_trainin

标签: 4595连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司