ShapeNet数据集及dataset代码分析

1数据集简介
ShpaeNet是点云中一个比较常见的数据集,它能够完成部件分割任务,即部件知道这个点云数据大的分割,还要将它的小部件进行分割。它总共包括十六个大的类别,每个大的类别有可以分成若干个小类别(例如,飞机可以分成机翼,身体等小类别),总共有五十个小类别。下面可视化一下,经过采样和上色后它长什么样子:
在这里插入图片描述
在这里插入图片描述

可以发现,它不仅将桌子和椅子进行了分割,还对它的桌子腿等小部件也分割为不同的颜色。

2.数据集结构
在这里插入图片描述
下载好数据集之后,数据集就是这样,其中,数字文件夹里面放的都是每个大类的点云数据。例如,第一个就是飞机大类。
在这里插入图片描述
打开其中的文件夹,可以发现里面是很多txt文件。每个txt文件是一个点云数据,相当于2d里面的一张图像。每个点云数据由很多点组成,其中前三个点是xyz,点云的位置坐标,后三个点是点云的rgb颜色坐标。对于shapenet,最好一个点是这个点所属的小类别,即1表示所属50个小类别中的第一个。
其它的文件夹的形式与这个都一样,这里就不过多详细叙述了。
在这里插入图片描述
对于train_test_split文件夹是一个划分数据的jason 文件。它将数据集划分为训练集 测试集 和验证集。每个元素都是一个点云数据,按斜杠划分第二个是该点云数据所属的类别,第三个是该点云数据的名称。例如,在test jason文件中的名称 ,就是用来测试的点云数据的名称。
在这里插入图片描述
synsetoffset2category.txt 里面存放的就是shapnet 十六个大类别与文件夹名称的对于关系。

3.Datasets 读入代码分析
关于shapenet数据读入的数据集,我参考的是PotinNet的数据集部分。
在这里插入图片描述
填写好路径,先来测试一些输出部分。可以发现,shapenet数据集会给我们返回三个输出。第一个就是每个点云集合下采样后 的xyz坐标,每个大类别的标签,以及每个点云集中每个点的类别(2,2500)。
下面简要分析一下数据集的代码,关键的代码我都已经做了注释

# *_*coding:utf-8 *_*
import os
import json
import warnings
import numpy as np
from torch.utils.data import Dataset
warnings.filterwarnings('ignore')

def pc_normalize(pc):
    centroid = np.mean(pc, axis=0)
    pc = pc - centroid
    m = np.max(np.sqrt(np.sum(pc ** 2, axis=1)))
    pc = pc / m
    return pc

class PartNormalDataset(Dataset):
    def __init__(self,root = './data/shapenetcore_partanno_segmentation_benchmark_v0_normal', npoints=2500, split='train', class_choice=None, normal_channel=False):
        self.npoints = npoints # 采样点数
        self.root = root # 文件根路径
        self.catfile = os.path.join(self.root, 'synsetoffset2category.txt') # 类别和文件夹名字对应的路径
        self.cat = {}
        self.normal_channel = normal_channel # 是否使用rgb信息


        with open(self.catfile, 'r') as f:
            for line in f:
                ls = line.strip().split()
                self.cat[ls[0]] = ls[1]
        self.cat = {k: v for k, v in self.cat.items()} #{'Airplane': '02691156', 'Bag': '02773838', 'Cap': '02954340', 'Car': '02958343', 'Chair': '03001627', 'Earphone': '03261776', 'Guitar': '03467517', 'Knife': '03624134', 'Lamp': '03636649', 'Laptop': '03642806', 'Motorbike': '03790512', 'Mug': '03797390', 'Pistol': '03948459', 'Rocket': '04099429', 'Skateboard': '04225987', 'Table': '04379243'}
        self.classes_original = dict(zip(self.cat, range(len(self.cat)))) #{'Airplane': 0, 'Bag': 1, 'Cap': 2, 'Car': 3, 'Chair': 4, 'Earphone': 5, 'Guitar': 6, 'Knife': 7, 'Lamp': 8, 'Laptop': 9, 'Motorbike': 10, 'Mug': 11, 'Pistol': 12, 'Rocket': 13, 'Skateboard': 14, 'Table': 15}

        if not class_choice is  None:  # 选择一些类别进行训练  好像没有使用这个功能
            self.cat = {k:v for k,v in self.cat.items() if k in class_choice}
        # print(self.cat)

        self.meta = {} # 读取分好类的文件夹jason文件 并将他们的名字放入列表中
        with open(os.path.join(self.root, 'train_test_split', 'shuffled_train_file_list.json'), 'r') as f:
            train_ids = set([str(d.split('/')[2]) for d in json.load(f)]) # '928c86eabc0be624c2bf2dcc31ba1713' 这是第一个值
        with open(os.path.join(self.root, 'train_test_split', 'shuffled_val_file_list.json'), 'r') as f:
            val_ids = set([str(d.split('/')[2]) for d in json.load(f)])
        with open(os.path.join(self.root, 'train_test_split', 'shuffled_test_file_list.json'), 'r') as f:
            test_ids = set([str(d.split('/')[2]) for d in json.load(f)])
        for item in self.cat:
            self.meta[item] = []
            dir_point = os.path.join(self.root, self.cat[item]) # # 拿到对应一个文件夹的路径 例如第一个文件夹02691156
            fns = sorted(os.listdir(dir_point))  # 根据路径拿到文件夹下的每个txt文件 放入列表中
            # print(fns[0][0:-4])
            if split == 'trainval':
                fns = [fn for fn in fns if ((fn[0:-4] in train_ids) or (fn[0:-4] in val_ids))]
            elif split == 'train':
                fns = [fn for fn in fns if fn[0:-4] in train_ids] # 判断文件夹中的txt文件是否在 训练txt中,如果是,那么fns中拿到的txt文件就是这个类别中所有txt文件中需要训练的文件,放入fns中
            elif split == 'val':
                fns = [fn for fn in fns if fn[0:-4] in val_ids]
            elif split == 'test':
                fns = [fn for fn in fns if fn[0:-4] in test_ids]
            else:
                print('Unknown split: %s. Exiting..' % (split))
                exit(-1)

            # print(os.path.basename(fns))
            for fn in fns:
                "第i次循环  fns中拿到的是第i个文件夹中符合训练的txt文件夹的名字"
                token = (os.path.splitext(os.path.basename(fn))[0])
                self.meta[item].append(os.path.join(dir_point, token + '.txt'))  # 生成一个字典,将类别名字和训练的路径组合起来  作为一个大类中符合训练的数据
                #上面的代码执行完之后,就实现了将所有需要训练或验证的数据放入了一个字典中,字典的键是该数据所属的类别,例如飞机。值是他对应数据的全部路径
                #{Airplane:[路径1,路径2........]}
        #####################################################################################################################################################
        self.datapath = []
        for item in self.cat: # self.cat 是类别名称和文件夹对应的字典
            for fn in self.meta[item]:
                self.datapath.append((item, fn)) # 生成标签和点云路径的元组, 将self.met 中的字典转换成了一个元组

        self.classes = {}
        for i in self.cat.keys():
            self.classes[i] = self.classes_original[i]
        ## self.classes  将类别的名称和索引对应起来  例如 飞机 <----> 0
        # Mapping from category ('Chair') to a list of int [10,11,12,13] as segmentation labels
        """
        shapenet 有16 个大类,然后每个大类有一些部件 ,例如飞机 'Airplane': [0, 1, 2, 3] 其中标签为0 1  2 3 的四个小类都属于飞机这个大类
        self.seg_classes 就是将大类和小类对应起来
        """
        self.seg_classes = {'Earphone': [16, 17, 18], 'Motorbike': [30, 31, 32, 33, 34, 35], 'Rocket': [41, 42, 43],
                            'Car': [8, 9, 10, 11], 'Laptop': [28, 29], 'Cap': [6, 7], 'Skateboard': [44, 45, 46],
                            'Mug': [36, 37], 'Guitar': [19, 20, 21], 'Bag': [4, 5], 'Lamp': [24, 25, 26, 27],
                            'Table': [47, 48, 49], 'Airplane': [0, 1, 2, 3], 'Pistol': [38, 39, 40],
                            'Chair': [12, 13, 14, 15], 'Knife': [22, 23]}

        # for cat in sorted(self.seg_classes.keys()):
        #     print(cat, self.seg_classes[cat])

        self.cache = {}  # from index to (point_set, cls, seg) tuple
        self.cache_size = 20000


    def __getitem__(self, index):
        if index in self.cache: # 初始slef.cache为一个空字典,这个的作用是用来存放取到的数据,并按照(point_set, cls, seg)放好 同时避免重复采样
            point_set, cls, seg = self.cache[index]
        else:
            fn = self.datapath[index] # 根据索引 拿到训练数据的路径self.datepath是一个元组(类名,路径)
            cat = self.datapath[index][0] # 拿到类名
            cls = self.classes[cat] # 将类名转换为索引
            cls = np.array([cls]).astype(np.int32)
            data = np.loadtxt(fn[1]).astype(np.float32) # size 20488,7 读入这个txt文件,共20488个点,每个点xyz rgb +小类别的标签
            if not self.normal_channel:  # 判断是否使用rgb信息
                point_set = data[:, 0:3]
            else:
                point_set = data[:, 0:6]
            seg = data[:, -1].astype(np.int32) # 拿到小类别的标签
            if len(self.cache) < self.cache_size:
                self.cache[index] = (point_set, cls, seg)
        point_set[:, 0:3] = pc_normalize(point_set[:, 0:3]) # 做一个归一化

        choice = np.random.choice(len(seg), self.npoints, replace=True) # 对一个类别中的数据进行随机采样 返回索引,允许重复采样
        # resample
        point_set = point_set[choice, :] # 根据索引采样
        seg = seg[choice]

        return point_set, cls, seg # pointset是点云数据,cls十六个大类别,seg是一个数据中,不同点对应的小类别

    def __len__(self):
        return len(self.datapath)

if __name__ == '__main__':
    import torch

    root = r'D:\1Apython\Pycharm_pojie\3d\Pointnet_Pointnet2_pytorch-master\data\shapenetcore_partanno_segmentation_benchmark_v0_normal'
    "测试一下sharpnet数据集"
    data =  PartNormalDataset(root=root)
    DataLoader = torch.utils.data.DataLoader(data, batch_size=2, shuffle=False)
    for point in DataLoader:
        print('point0.shape:\n', point[0].shape) # ([2, 2500, 3])
        print('point1.shape:\n', point[1].shape) # [2, 1])  大部件的标签
        print('point2.shape:\n', point[2].shape)  # torch.Size([2, 2500])  部件类别标签,每个点的标签
        #print('label.shape:\n', label.shape)

更改数据集后,应该修改如下地方
1、在这里插入图片描述
2、在这里插入图片描述
3、在这里插入图片描述
4、在这里插入图片描述
5、5种类型焊缝的点云分割训练结果
eval mIoU of Fillet 0.494629
eval mIoU of PlaneLap 0.494873
eval mIoU of PlaneV 0.492920
eval mIoU of SpaceLap 0.495117
eval mIoU of SpaceV 0.362541
Epoch 251 test Accuracy: 0.931445 Class avg mIOU: 0.468016 Inctance avg mIOU: 0.468016
Best accuracy is: 0.98242
Best class avg mIOU is: 0.51867
Best inctance avg mIOU is: 0.51867

测试结果:
eval mIoU of Fillet 0.496826
eval mIoU of PlaneLap 0.493652
eval mIoU of PlaneV 0.493408
eval mIoU of SpaceLap 0.496094
eval mIoU of SpaceV 0.209798
Accuracy is: 0.91787
Class avg accuracy is: 0.45455
Class avg mIOU is: 0.43796
Inctance avg mIOU is: 0.43796
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
to_categorical就是将类别向量转换为二进制(只有0和1)的矩阵类型表示。其表现为将原有的类别向量转换为独热编码的形式。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值