S3DIS数据集学习笔记

S3DIS数据集学习笔记S3DIS 是一个大型的 3d 室内数据集 关于它的介绍网上很多了 它主要是由 xyz rgb 法向量 9 个特征和一个标签组成的 长度为 10 的向量 由于这个数据集比较大 不利于训练 PointNet 训练之前会将它进行预处理 划分为 1mx1m 的区域 然后再放入网络进行训练 下面是关于它的代码分析 这个数据集运行的前提是 你已经按照 PointNet 的 readme 运行了 collect indor3d data 将 txt 文件转换为了 npy 文件 classS3DISDa Dataset

1.整体介绍

2.数据集代码分析

由于这个数据集比较大,不利于训练,PointNet++训练之前会将它进行预处理。对每个房间点的数量对其进行随机采样,随机划分划分为1mx1m的block,然后再放入网络进行训练。

对于数据集的划分,又牵扯到非常多的问题了,例如 如何采样才能使选出的点云信息尽可能少丢失,如何才能保证覆盖到全部点,将大点云分割多少个小点云比较合适,这些都是值得思考的问题。

3.初始化方法

 def __init__(self, split='train', data_root='trainval_fullarea', num_point=4096, test_area=5, block_size=1.0, sample_rate=1.0, transform=None): super().__init__() self.num_point = num_point # 4096 self.block_size = block_size # 1.0 self.transform = transform rooms = sorted(os.listdir(data_root)) # data_root = 'data/s3dis/stanford_indoor3d/' rooms = [room for room in rooms if 'Area_' in room] # 'Area_1_WC_1.npy' # 'Area_1_conferenceRoom_1.npy' "rooms里面存放的是之前转换好的npy数据的名字,例如:Area_1_conferenceRoom1.npy....这样的数据" if split == 'train': rooms_split = [room for room in rooms if not 'Area_{}'.format(test_area) in room] # area 1,2,3,4,6为训练区域,5为测试区域 else: rooms_split = [room for room in rooms if 'Area_{}'.format(test_area) in room] "按照指定的test_area划分为训练集和测试集,默认是将区域5作为测试集" #创建一些储存数据的列表 self.room_points, self.room_labels = [], [] # 每个房间的点云和标签 self.room_coord_min, self.room_coord_max = [], [] # 每个房间的最大值和最小值 num_point_all = [] # 初始化每个房间点的总数的列表 labelweights = np.zeros(13) # 初始标签权重,后面用来统计标签的权重 

这一部分内容比较简单,就是一些简单的赋值和容器的定义,这里就不做过多详细的介绍。初始化函数接下来的部分可以回答上面的部分问题了。

 #每层初始化数据集的时候会执行以下代码 for room_name in tqdm(rooms_split, total=len(rooms_split)): #每次拿到的room_namej就是之前划分好的'Area_1_WC_1.npy' room_path = os.path.join(data_root, room_name) #每个小房间的绝对路径,根路径+.npy # .astype(np.long) # 加载数据 xyzrgbl, (, 7) N*7 room中点云的值 最后一个是标签# room_data = np.load(room_path).astype(np.float32) points, labels = room_data[:, 0:6], room_data[:, 6] # xyzrgb, N*6; l, N 将训练数据与标签分开 #self.p ,self.l = points,labels "前面已经将标签进行了分离,那么这里 np.histogram就是统计每个房间里所有标签的总数,例如,第一个元素就是属于类别0的点的总数" "将数据集所有点统计一次之后,就知道每个类别占总类别的比例,为后面加权计算损失做准备" tmp, _ = np.histogram(labels, range(14)) # 统计标签的分布情况 [ 0 0 0 28008 0 0 0, 0 0 ] #也就是有多少个点属于第i个类别 labelweights += tmp # 将它们累计起来 #coord_min ,coord_max 寻找该房间中所有xyz的最小值和最大值 其输出形状为3 coord_min, coord_max = np.amin(points, axis=0)[:3], np.amax(points, axis=0)[:3] # 获取当前房间坐标的最值 self.room_points.append(points), self.room_labels.append(labels) self.room_coord_min.append(coord_min), self.room_coord_max.append(coord_max) num_point_all.append(labels.size) # 标签的数量 也就是点的数量 第一次循环这里的值为 "通过for循环后,所有的房间里类别分布情况和坐标情况都被放入了相应的变量中,后面就是计算权重了" 
 labelweights = labelweights.astype(np.float32) labelweights = labelweights / np.sum(labelweights) # 计算标签的权重,每个类别的点云总数/总的点云总数 "感觉这里应该是为了避免有的点数量比较少,计算出训练的iou占miou的比重太大,所以在这里计算一下加权(根据点标签的数量进行加权)" self.labelweights = np.power(np.amax(labelweights) / labelweights, 1 / 3.0) print('label weight\n') #通过上述操作又将labelweights缩放到大于 1 的数了 print(self.labelweights) sample_prob = num_point_all / np.sum(num_point_all) # 每个房间的点数占所有房间的点数之比 也就是需要采样的比例,比重越大 采样的点越多 num_iter = int(np.sum(num_point_all) * sample_rate / num_point) # 如果按 sample rate进行采样,那么每个区域用4096个点 计算需要采样的次数,值为476 

对整个数据集,我们应该将其划分为多少个子点云? 可以看到,num_iter = int(np.sum(num_point_all) * sample_rate / num_point) 一行代码就是用来计算应该划分为多少个子点云,它的含义就是将数据集的点数求和然后乘采样概率(超参数,这里为0.01)然后除以采样后子区域的点数(4096)

那么每个小房间又应该如何采样呢,或者说,总不能对所有对所有房间一同对待吧(因为每个房间的点数就不一样)
代码中首先计算了sample_prob(采样概率),即每个小房间的点数占整个数据集点数的比例,如果该小房间点数多,我们对应应该多采样点,如果小应该少采样。 然后根据这个比例来计算每个小房间总共需要划分多少个子点云,这个变量很关键,后面会用到。

 room_idxs = [] # 该处计算按照上面的划分规则,每个子区域需要采样多少次,才能将整个区域分完 # 例如 假如第一个房间按照上述规则能够被划分七个区域 那么index里面的值 应该有7个0 其中0表示第一个区域 7 表示能分成几块 for index in range(len(rooms_split)): room_idxs.extend([index] * int(round(sample_prob[index] * num_iter))) self.room_idxs = np.array(room_idxs) print("Totally {} samples in {} set.".format(len(self.room_idxs), split)) 

这段代码非常有用,它是用来决定每个小房间应该采样多少个子点云的。它的计算方式如下:rooms_split的长度为小房间的数量,即需要训练的数据。用每个房间采样的概率乘以总共 划分的房间数,就可以得到该小房间能够被划分几次,然后去乘以index,就能将它复制多少份。例如 假如第一个房间按照上述规则能够被划分七个区域 那么index里面的值 应该有7个0 其中0表示第一个区域 7 表示能分成几块。

上面就是整个初始化函数里面做的事情了,变量比较多,也比较复杂,其中重要的变量已经进行了详细的解释,请读者仔细思考一下带self的类变量的含义,以便更好的分析getitem方法。

4. getitem方法

getitem中解决的就是如何读取数据的问题,即如何具体的划分数据。接下来还是一段一段的带着大家进行分析。

 def __getitem__(self, idx): #通过前面的分析,已经self.room_idxs中存放的是每个小房间应该划分为几个子区域 room_idx = self.room_idxs[idx] points = self.room_points[room_idx] # N * 6 --》 debug ,6 labels = self.room_labels[room_idx] # N N_points = points.shape[0] 
 while (True): # 这里是不是对应的就是将一个房间的点云切分为一个区域 center = points[np.random.choice(N_points)][:3] #从该个房间随机选一个点作为中心点 block_min = center - [self.block_size / 2.0, self.block_size / 2.0, 0] block_max = center + [self.block_size / 2.0, self.block_size / 2.0, 0] "找到符合要求点的索引(min<=x,y,z<=max),坐标被限制在最小和最大值之间" point_idxs = np.where((points[:, 0] >= block_min[0]) & (points[:, 0] <= block_max[0]) & (points[:, 1] >= block_min[1]) & (points[:, 1] <= block_max[1]))[0] "如果符合要求的点至少有1024个,那么跳出循环,否则继续随机选择中心点,继续寻找" if point_idxs.size > 1024: break 

在这里插入图片描述
从上图中可以发现,第一次就能找到40016个符合条件的点。

 if point_idxs.size >= self.num_point: # 如果找到符合条件的点大于给定的4096个点,那么随机采样4096个点作为被选择的点 selected_point_idxs = np.random.choice(point_idxs, self.num_point, replace=False) else:# 如果符合条件的点小于4096 则随机重复采样凑够4096个点 selected_point_idxs = np.random.choice(point_idxs, self.num_point, replace=True) # 

这部分就是来处理采样点多了或少了的问题。采样点过多则随机抽取,不够则重复选择。

 # normalize selected_points = points[selected_point_idxs, :] # num_point * 6 拿到筛选后的4096个点 current_points = np.zeros((self.num_point, 9)) # num_point * 9 current_points[:, 6] = selected_points[:, 0] / self.room_coord_max[room_idx][0] # 选择点的坐标/被选择房间的最大值 做坐标的归一化 current_points[:, 7] = selected_points[:, 1] / self.room_coord_max[room_idx][1] current_points[:, 8] = selected_points[:, 2] / self.room_coord_max[room_idx][2] selected_points[:, 0] = selected_points[:, 0] - center[0] # 再将坐标移至随机采样的中心点 selected_points[:, 1] = selected_points[:, 1] - center[1] selected_points[:, 3:6] /= 255.0 # 颜色信息归一化 current_points[:, 0:6] = selected_points current_labels = labels[selected_point_idxs] if self.transform is not None: current_points, current_labels = self.transform(current_points, current_labels) return current_points, current_labels 

这一部分代码就比较容易了,主要就是对s3dis的后三维坐标进行补充,中心坐标的偏移,颜色信息的归一化和标签的选择。

以上就是我对s3dis数据集代码的理解,这个里面貌似有些变量没有使用,例如labelweight,我也不知道是干嘛的,知道的同学可以告诉我一下。

下面是完整的代码

class S3DISDataset(Dataset): def __init__(self, split='train', data_root='trainval_fullarea', num_point=4096, test_area=5, block_size=1.0, sample_rate=1.0, transform=None): super().__init__() self.num_point = num_point # 4096 self.block_size = block_size # 1.0 self.transform = transform rooms = sorted(os.listdir(data_root)) # data_root = 'data/s3dis/stanford_indoor3d/' rooms = [room for room in rooms if 'Area_' in room] # 'Area_1_WC_1.npy' # 'Area_1_conferenceRoom_1.npy' "rooms里面存放的是之前转换好的npy数据的名字,例如:Area_1_conferenceRoom1.npy....这样的数据" if split == 'train': rooms_split = [room for room in rooms if not 'Area_{}'.format(test_area) in room] # area 1,2,3,4,6为训练区域,5为测试区域 else: rooms_split = [room for room in rooms if 'Area_{}'.format(test_area) in room] "按照指定的test_area划分为训练集和测试集,默认是将区域5作为测试集" #创建一些储存数据的列表 self.room_points, self.room_labels = [], [] # 每个房间的点云和标签 self.room_coord_min, self.room_coord_max = [], [] # 每个房间的最大值和最小值 num_point_all = [] # 初始化每个房间点的总数的列表 labelweights = np.zeros(13) # 初始标签权重,后面用来统计标签的权重 #每层初始化数据集的时候会执行以下代码 for room_name in tqdm(rooms_split, total=len(rooms_split)): #每次拿到的room_namej就是之前划分好的'Area_1_WC_1.npy' room_path = os.path.join(data_root, room_name) #每个小房间的绝对路径,根路径+.npy # .astype(np.long) # 加载数据 xyzrgbl, (, 7) N*7 room中点云的值 最后一个是标签# room_data = np.load(room_path).astype(np.float32) points, labels = room_data[:, 0:6], room_data[:, 6] # xyzrgb, N*6; l, N 将训练数据与标签分开 #self.p ,self.l = points,labels "前面已经将标签进行了分离,那么这里 np.histogram就是统计每个房间里所有标签的总数,例如,第一个元素就是属于类别0的点的总数" "将数据集所有点统计一次之后,就知道每个类别占总类别的比例,为后面加权计算损失做准备" tmp, _ = np.histogram(labels, range(14)) # 统计标签的分布情况 [ 0 0 0 28008 0 0 0, 0 0 ] #也就是有多少个点属于第i个类别 labelweights += tmp # 将它们累计起来 #coord_min ,coord_max 寻找该房间中所有xyz的最小值和最大值 其输出形状为3 coord_min, coord_max = np.amin(points, axis=0)[:3], np.amax(points, axis=0)[:3] # 获取当前房间坐标的最值 self.room_points.append(points), self.room_labels.append(labels) self.room_coord_min.append(coord_min), self.room_coord_max.append(coord_max) num_point_all.append(labels.size) # 标签的数量 也就是点的数量 第一次循环这里的值为 "通过for循环后,所有的房间里类别分布情况和坐标情况都被放入了相应的变量中,后面就是计算权重了" labelweights = labelweights.astype(np.float32) labelweights = labelweights / np.sum(labelweights) # 计算标签的权重,每个类别的点云总数/总的点云总数 "感觉这里应该是为了避免有的点数量比较少,计算出训练的iou占miou的比重太大,所以在这里计算一下加权(根据点标签的数量进行加权)" self.labelweights = np.power(np.amax(labelweights) / labelweights, 1 / 3.0) print('label weight\n') #通过上述操作又将labelweights缩放到大于 1 的数了 print(self.labelweights) sample_prob = num_point_all / np.sum(num_point_all) # 每个房间的点数占所有房间的点数之比 也就是需要采样的比例,比重越大 采样的点越多 num_iter = int(np.sum(num_point_all) * sample_rate / num_point) # 如果按 sample rate进行采样,那么每个区域用4096个点 计算需要采样的次数,值为476 room_idxs = [] # 该处计算按照上面的划分规则,每个子区域需要采样多少次,才能将整个区域分完 # 例如 假如第一个房间按照上述规则能够被划分七个区域 那么index里面的值 应该有7个0 其中0表示第一个区域 7 表示能分成几块 for index in range(len(rooms_split)): room_idxs.extend([index] * int(round(sample_prob[index] * num_iter))) self.room_idxs = np.array(room_idxs) print("Totally {} samples in {} set.".format(len(self.room_idxs), split)) def __getitem__(self, idx): #通过前面的分析,已经self.room_idxs中存放的是每个小房间应该划分为几个子区域 room_idx = self.room_idxs[idx] points = self.room_points[room_idx] # N * 6 --》 debug ,6 labels = self.room_labels[room_idx] # N N_points = points.shape[0] while (True): # 这里是不是对应的就是将一个房间的点云切分为一个区域 center = points[np.random.choice(N_points)][:3] #从该个房间随机选一个点作为中心点 block_min = center - [self.block_size / 2.0, self.block_size / 2.0, 0] block_max = center + [self.block_size / 2.0, self.block_size / 2.0, 0] "找到符合要求点的索引(min<=x,y,z<=max),坐标被限制在最小和最大值之间" point_idxs = np.where((points[:, 0] >= block_min[0]) & (points[:, 0] <= block_max[0]) & (points[:, 1] >= block_min[1]) & (points[:, 1] <= block_max[1]))[0] "如果符合要求的点至少有1024个,那么跳出循环,否则继续随机选择中心点,继续寻找" if point_idxs.size > 1024: break "这里可以尝试修改一下1024这个参数,感觉采4096个点的话,可能存在太多重复的点" if point_idxs.size >= self.num_point: # 如果找到符合条件的点大于给定的4096个点,那么随机采样4096个点作为被选择的点 selected_point_idxs = np.random.choice(point_idxs, self.num_point, replace=False) else:# 如果符合条件的点小于4096 则随机重复采样凑够4096个点 selected_point_idxs = np.random.choice(point_idxs, self.num_point, replace=True) # # normalize selected_points = points[selected_point_idxs, :] # num_point * 6 拿到筛选后的4096个点 current_points = np.zeros((self.num_point, 9)) # num_point * 9 current_points[:, 6] = selected_points[:, 0] / self.room_coord_max[room_idx][0] # 选择点的坐标/被选择房间的最大值 做坐标的归一化 current_points[:, 7] = selected_points[:, 1] / self.room_coord_max[room_idx][1] current_points[:, 8] = selected_points[:, 2] / self.room_coord_max[room_idx][2] selected_points[:, 0] = selected_points[:, 0] - center[0] # 再将坐标移至随机采样的中心点 selected_points[:, 1] = selected_points[:, 1] - center[1] selected_points[:, 3:6] /= 255.0 # 颜色信息归一化 current_points[:, 0:6] = selected_points current_labels = labels[selected_point_idxs] if self.transform is not None: current_points, current_labels = self.transform(current_points, current_labels) return current_points, current_labels def __len__(self): return len(self.room_idxs) 

整体流程

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/176431.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午9:57
下一篇 2026年3月26日 下午9:57


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号