当前位置:   article > 正文

【转载】细嚼慢咽读论文:点云上采样GAN的实践——PU-GAN_pu-gan: a point cloud upsampling adversarial netwo

pu-gan: a point cloud upsampling adversarial network

论文标题:PU-GAN: a Point Cloud Upsampling Adversarial Network

标签:有监督 | 点云上采样


首先我们来分析一下文章题目:PU-GAN: a Point Cloud Upsampling Adversarial Network

PU即Point Upsampling,也就是本文要做的任务是点云上采样。关于点云上采样的介绍,我在介绍PU-Net的这篇文章中介绍过,可参考:

刘昕宸:细嚼慢咽读论文:点云上采样网络开天辟地PU-Net​zhuanlan.zhihu.com

GAN即现在大名鼎鼎的GAN(生成对抗网络),也就是本文使用的网络是GAN,依赖GAN来实现点云的上采样。上采样任务其实也是一种生成式任务,因此很自然地想到可以使用GAN来尝试一下。关于GAN的基本原理介绍,可参考:

刘昕宸:通俗理解GAN(一):把GAN给你讲得明明白白​zhuanlan.zhihu.com图标


1 motivation

上采样的意义我在PU-Net那篇文章中详细介绍过:

点云处理任务存在极大挑战,很重要的一点是点云这种数据形式的稀疏性和不规则性。
而本文要做的上采样任务,正是为了解决点云数据稀疏性这一问题,为下游各种特征学习任务提供更“高质”的数据。
点云上采样任务,简单来说就是输入某一点云,生成保持基本形状的“更稠密”点云。

单就上采样效果而言,之前基于深度学习的方法如PU-NetMPU在现实场景扫描点云上取得的效果均非常有限。我们来看看PU-GAN论文在开头放的图(Kitti数据集上测试):

点云上采样本质也是一种生成式任务,在视觉领域做生成任务,自然而然地就会想到:不妨试试GAN??

2 contribution

  1. 针对点云上采样任务,提出了GAN框架的解决方案,并且取得了非常好的效果。(原文指出:the difficulty to balance between the generator and discriminator and to avoid the tendency of poor convergence.)
  2. 局部网络结构设计非常有新意:比如up-down-up unit用来expand point features,self-attention unit用来feature integration quality等
  3. 设计了compound loss,特别是设计了用来约束上采样点云均匀分布uniform loss,让人眼前一亮。
  4. PU-GAN不仅在一般点云模型上做了实验,还在KITTI这样真实扫描的场景点云上做了上采样实验,依然取得了非常好的效果,这也进一步验证了PU-GAN强大的泛化能力。

3 solution

本文的目标就是上采样,也就是给定有N个点的稀疏点集  P=\left \{ p_{i} \right \}_{i=1}^{N},我们期望生成有 rN 个点的稠密点集Q=\left \{ q_{i} \right \}_{i=1}^{rN} .

Q 并不需要是 P 的超集,但是需要满足以下2个条件:

  1. Q 应该能够和 P 表达一样的underlying geometry of latent target object.
  2. Q 内的点应该是在target object surface上均匀分布的,即使甚至输入 P 都是非均匀的。

PU-GAN的网络结构图如下所示:

因为是GAN,所以网络分成了Generator和Discriminator两部分。

Generator用于从稀疏点云 P 生成稠密点云 Q .

Discriminator用于区分真实稠密点云和generator生成的点云

3.1 Generator

看出来了嘛,其实generator的整体框架还是PU-Net那一套patch --> feature extraction --> feature expansion --> coordinate reconstruction ;-)

Generator全局代码:

  1. class Generator(object):
  2. def __init__(self, opts,is_training, name="Generator"):
  3. self.opts = opts
  4. self.is_training = is_training
  5. self.name = name
  6. self.reuse = False
  7. self.num_point = self.opts.patch_num_point
  8. self.up_ratio = self.opts.up_ratio
  9. self.up_ratio_real = self.up_ratio + self.opts.more_up
  10. self.out_num_point = int(self.num_point*self.up_ratio)
  11. def __call__(self, inputs):
  12. with tf.variable_scope(self.name, reuse=self.reuse):
  13. features = ops.feature_extraction(inputs, scope='feature_extraction', is_training=self.is_training, bn_decay=None)
  14. H = ops.up_projection_unit(features, self.up_ratio_real, scope="up_projection_unit", is_training=self.is_training, bn_decay=None)
  15. coord = ops.conv2d(H, 64, [1, 1],
  16. padding='VALID', stride=[1, 1],
  17. bn=False, is_training=self.is_training,
  18. scope='fc_layer1', bn_decay=None)
  19. coord = ops.conv2d(coord, 3, [1, 1],
  20. padding='VALID', stride=[1, 1],
  21. bn=False, is_training=self.is_training,
  22. scope='fc_layer2', bn_decay=None,
  23. activation_fn=None, weight_decay=0.0)
  24. outputs = tf.squeeze(coord, [2])
  25. outputs = gather_point(outputs, farthest_point_sample(self.out_num_point, outputs))
  26. self.reuse = True
  27. self.variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, self.name)
  28. return outputs

Ⅰ Patch Extraction

对每个3D mesh,在表面随机选择200个种子点,对每个种子点根据测地线距离生成patch,并将每个patch normalize到一个unit sphere中。

对每个patch,使用Poisson Disk Sampling生成 \hat{Q} ,作为有 rN 个点的目标点云

我们动态地对 \hat{Q} 随机采样 N 个点生成输入点云 P .

 

Ⅱ Feature Extraction

本模块旨在提取point-wise feature:

输入点云 N*d ( d 包括点云的原始数据,坐标、颜色、法向量等, d 一般为 3 ),输出point-wise feature N*C

本模块直接借鉴了论文Patch-based progressive 3D point set upsampling特征提取方法,使用了dense connection集成不同层的特征。

网络结构如下,处理过程非常明晰了:

我们再来看看代码加深理解:

  1. def feature_extraction(inputs, scope='feature_extraction2', is_training=True, bn_decay=None):
  2. with tf.variable_scope(scope,reuse=tf.AUTO_REUSE):
  3. use_bn = False
  4. use_ibn = False
  5. growth_rate = 24
  6. dense_n = 3
  7. knn = 16
  8. comp = growth_rate*2
  9. l0_features = tf.expand_dims(inputs, axis=2)
  10. l0_features = conv2d(l0_features, 24, [1, 1],
  11. padding='VALID', scope='layer0', is_training=is_training, bn=use_bn, ibn=use_ibn,
  12. bn_decay=bn_decay, activation_fn=None)
  13. l0_features = tf.squeeze(l0_features, axis=2)
  14. # encoding layer
  15. l1_features, l1_idx = dense_conv(l0_features, growth_rate=growth_rate, n=dense_n, k=knn,
  16. scope="layer1", is_training=is_training, bn=use_bn, ibn=use_ibn,
  17. bn_decay=bn_decay)
  18. l1_features = tf.concat([l1_features, l0_features], axis=-1) # (12+24*2)+24=84
  19. l2_features = conv1d(l1_features, comp, 1, # 24
  20. padding='VALID', scope='layer2_prep', is_training=is_training, bn=use_bn, ibn=use_ibn,
  21. bn_decay=bn_decay)
  22. l2_features, l2_idx = dense_conv(l2_features, growth_rate=growth_rate, n=dense_n, k=knn,
  23. scope="layer2", is_training=is_training, bn=use_bn, bn_decay=bn_decay)
  24. l2_features = tf.concat([l2_features, l1_features], axis=-1) # 84+(24*2+12)=144
  25. l3_features = conv1d(l2_features, comp, 1, # 48
  26. padding='VALID', scope='layer3_prep', is_training=is_training, bn=use_bn, ibn=use_ibn,
  27. bn_decay=bn_decay) # 48
  28. l3_features, l3_idx = dense_conv(l3_features, growth_rate=growth_rate, n=dense_n, k=knn,
  29. scope="layer3", is_training=is_training, bn=use_bn, bn_decay=bn_decay)
  30. l3_features = tf.concat([l3_features, l2_features], axis=-1) # 144+(24*2+12)=204
  31. l4_features = conv1d(l3_features, comp, 1, # 48
  32. padding='VALID', scope='layer4_prep', is_training=is_training, bn=use_bn, ibn=use_ibn,
  33. bn_decay=bn_decay) # 48
  34. l4_features, l3_idx = dense_conv(l4_features, growth_rate=growth_rate, n=dense_n, k=knn,
  35. scope="layer4", is_training=is_training, bn=use_bn, bn_decay=bn_decay)
  36. l4_features = tf.concat([l4_features, l3_features], axis=-1) # 204+(24*2+12)=264
  37. l4_features = tf.expand_dims(l4_features, axis=2)
  38. return l4_features

核心dense_conv的实现:

  1. def dense_conv(feature, n=3,growth_rate=64, k=16, scope='dense_conv',**kwargs):
  2. with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
  3. y, idx = get_edge_feature(feature, k=k, idx=None) # [B N K 2*C]
  4. for i in range(n):
  5. if i == 0:
  6. y = tf.concat([
  7. conv2d(y, growth_rate, [1, 1], padding='VALID', scope='l%d' % i, **kwargs),
  8. tf.tile(tf.expand_dims(feature, axis=2), [1, 1, k, 1])], axis=-1)
  9. elif i == n-1:
  10. y = tf.concat([
  11. conv2d(y, growth_rate, [1, 1], padding='VALID', scope='l%d' % i, activation_fn=None, **kwargs),
  12. y], axis=-1)
  13. else:
  14. y = tf.concat([
  15. conv2d(y, growth_rate, [1, 1], padding='VALID', scope='l%d' % i, **kwargs),
  16. y], axis=-1)
  17. y = tf.reduce_max(y, axis=-2)
  18. return y, idx

Ⅲ Feature Expansion

和PU-Net一样,PU-GAN也设计了自己的feature expansion模块,这也应该是上采样算法的核心了吧

PU-Net的做法是 直接复制点的特征,然后使用不同的 MLP来分别独立处理各自的点特征备份。
即使PU-Net使用了诸如 repulsion loss这样的约束,但这种上采样方式仍然会导致 扩展的点特征过于接近彼此,影响上采样质量。

输入point-wise feature N*C ,输出 rN*{C}'

PU-GAN还设计了up-down-up expansion unit增强特征扩展的效果,以实现enabling the generator to produce more diverse point distributions.

网络结构图如下所示,还包括了Up-feature operator和Down-feature operator的结构:

看代码是比较明晰的,点特征输入到Up-feature operator生成 H_{0} ,再输入Down-feature operator将其降采样回L_{0}.

计算降采样点特征与原输入之间的difference E_{0} .

输入E_{0}  到Up-feature operator生成  H_{1},将 H_{1}作为 H_{0} 的偏移量,得 H_{2}=H_{0}+H_{1} . 

  1. def up_projection_unit(inputs,up_ratio,scope="up_projection_unit",is_training=True,bn_decay=None):
  2. with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
  3. L = conv2d(inputs, 128, [1, 1],
  4. padding='VALID', stride=[1, 1],
  5. bn=False, is_training=is_training,
  6. scope='conv0', bn_decay=bn_decay)
  7. H0 = up_block(L,up_ratio,is_training=is_training,bn_decay=bn_decay,scope='up_0')
  8. L0 = down_block(H0,up_ratio,is_training=is_training,bn_decay=bn_decay,scope='down_0')
  9. E0 = L0-L
  10. H1 = up_block(E0,up_ratio,is_training=is_training,bn_decay=bn_decay,scope='up_1')
  11. H2 = H0+H1
  12. return H2

Up-feature operator

不像PU-Net直接复制,PU-GAN在复制点特征时使用了grid结构(可参考FoldingNet: Point Cloud Auto-encoder via Deep Grid Deformation),这等价于在输入点附近增加一些新的点.

整合复制点特征使用了self attention机制

  1. def up_block(inputs, up_ratio, scope='up_block', is_training=True, bn_decay=None):
  2. with tf.variable_scope(scope,reuse=tf.AUTO_REUSE):
  3. net = inputs
  4. dim = inputs.get_shape()[-1]
  5. out_dim = dim*up_ratio
  6. grid = gen_grid(up_ratio)
  7. grid = tf.tile(tf.expand_dims(grid, 0), [tf.shape(net)[0], 1,tf.shape(net)[1]]) # [batch_size, num_point*4, 2])
  8. grid = tf.reshape(grid, [tf.shape(net)[0], -1, 1, 2])
  9. #grid = tf.expand_dims(grid, axis=2)
  10. net = tf.tile(net, [1, up_ratio, 1, 1])
  11. net = tf.concat([net, grid], axis=-1)
  12. net = attention_unit(net, is_training=is_training)
  13. net = conv2d(net, 256, [1, 1],
  14. padding='VALID', stride=[1, 1],
  15. bn=False, is_training=is_training,
  16. scope='conv1', bn_decay=bn_decay)
  17. net = conv2d(net, 128, [1, 1],
  18. padding='VALID', stride=[1, 1],
  19. bn=False, is_training=is_training,
  20. scope='conv2', bn_decay=bn_decay)
  21. return net

1)grid机制

为每个feature-map copy生成一个唯一的2D vector,然后将该2D vector拼接给其对应feature-map copy内的每一个点。

因为该2D vector的存在,因此复制的点特征还是有些细微差别的。

  1. def gen_grid(up_ratio):
  2. import math
  3. """
  4. output [num_grid_point, 2]
  5. """
  6. sqrted = int(math.sqrt(up_ratio))+1
  7. for i in range(1,sqrted+1).__reversed__():
  8. if (up_ratio%i) == 0:
  9. num_x = i
  10. num_y = up_ratio//i
  11. break
  12. grid_x = tf.lin_space(-0.2, 0.2, num_x)
  13. grid_y = tf.lin_space(-0.2, 0.2, num_y)
  14. x, y = tf.meshgrid(grid_x, grid_y)
  15. grid = tf.reshape(tf.stack([x, y], axis=-1), [-1, 2]) # [2, 2, 2] -> [4, 2]
  16. return grid

2)attention机制

  1. def attention_unit(inputs, scope='attention_unit',is_training=True):
  2. with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
  3. dim = inputs.get_shape()[-1].value
  4. layer = dim//4
  5. f = conv2d(inputs,layer, [1, 1],
  6. padding='VALID', stride=[1, 1],
  7. bn=False, is_training=is_training,
  8. scope='conv_f', bn_decay=None)
  9. g = conv2d(inputs, layer, [1, 1],
  10. padding='VALID', stride=[1, 1],
  11. bn=False, is_training=is_training,
  12. scope='conv_g', bn_decay=None)
  13. h = conv2d(inputs, dim, [1, 1],
  14. padding='VALID', stride=[1, 1],
  15. bn=False, is_training=is_training,
  16. scope='conv_h', bn_decay=None)
  17. s = tf.matmul(hw_flatten(g), hw_flatten(f), transpose_b=True) # # [bs, N, N]
  18. beta = tf.nn.softmax(s, axis=-1) # attention map
  19. o = tf.matmul(beta, hw_flatten(h)) # [bs, N, N]*[bs, N, c]->[bs, N, c]
  20. gamma = tf.get_variable("gamma", [1], initializer=tf.constant_initializer(0.0))
  21. o = tf.reshape(o, shape=inputs.shape) # [bs, h, w, C]
  22. x = gamma * o + inputs
  23. return x

Down-feature operator

Down结构比较简单:

对expanded features降采样,对特征reshape,然后使用一系列MLPs来拟合原特征

  1. def down_block(inputs,up_ratio,scope='down_block',is_training=True,bn_decay=None):
  2. with tf.variable_scope(scope,reuse=tf.AUTO_REUSE):
  3. net = inputs
  4. net = tf.reshape(net,[tf.shape(net)[0],up_ratio,-1,tf.shape(net)[-1]])
  5. net = tf.transpose(net, [0, 2, 1, 3])
  6. net = conv2d(net, 256, [1, up_ratio],
  7. padding='VALID', stride=[1, 1],
  8. bn=False, is_training=is_training,
  9. scope='conv1', bn_decay=bn_decay)
  10. net = conv2d(net, 128, [1, 1],
  11. padding='VALID', stride=[1, 1],
  12. bn=False, is_training=is_training,
  13. scope='conv2', bn_decay=bn_decay)
  14. return net

Ⅳ Coordinate Reconstruction

最后是坐标重建

  1. coord = ops.conv2d(H, 64, [1, 1],
  2. padding='VALID', stride=[1, 1],
  3. bn=False, is_training=self.is_training,
  4. scope='fc_layer1', bn_decay=None)
  5. coord = ops.conv2d(coord, 3, [1, 1],
  6. padding='VALID', stride=[1, 1],
  7. bn=False, is_training=self.is_training,
  8. scope='fc_layer2', bn_decay=None,
  9. activation_fn=None, weight_decay=0.0)
  10. outputs = tf.squeeze(coord, [2])

3.2 Discriminator

Discriminator的目标是分辨上采样点云是否是Generator生成的

首先使用一个轻量的网络结构整合local和global信息提取global feature

另外Discriminator还使用了self-attention unitenhance the feature integration and improve the subsequent feature extraction capability

最后使用MLP和pooling得到了最后的confidence value,可以理解成是Discriminator判断输入上采样点云是真实上采样点云的可能性。

  1. class Discriminator(object):
  2. def __init__(self, opts,is_training, name="Discriminator"):
  3. self.opts = opts
  4. self.is_training = is_training
  5. self.name = name
  6. self.reuse = False
  7. self.bn = False
  8. self.start_number = 32
  9. #print('start_number:',self.start_number)
  10. def __call__(self, inputs):
  11. with tf.variable_scope(self.name, reuse=self.reuse):
  12. inputs = tf.expand_dims(inputs,axis=2)
  13. with tf.variable_scope('encoder_0', reuse=tf.AUTO_REUSE):
  14. features = ops.mlp_conv(inputs, [self.start_number, self.start_number * 2])
  15. features_global = tf.reduce_max(features, axis=1, keep_dims=True, name='maxpool_0')
  16. features = tf.concat([features, tf.tile(features_global, [1, tf.shape(inputs)[1],1, 1])], axis=-1)
  17. features = ops.attention_unit(features, is_training=self.is_training)
  18. with tf.variable_scope('encoder_1', reuse=tf.AUTO_REUSE):
  19. features = ops.mlp_conv(features, [self.start_number * 4, self.start_number * 8])
  20. features = tf.reduce_max(features, axis=1, name='maxpool_1')
  21. with tf.variable_scope('decoder', reuse=tf.AUTO_REUSE):
  22. outputs = ops.mlp(features, [self.start_number * 8, 1])
  23. outputs = tf.reshape(outputs, [-1, 1])
  24. self.reuse = True
  25. self.variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, self.name)
  26. return outputs

3.3 Loss

因为GAN的原因,PU-GAN的loss设计得比较多,主要分为了Generator loss和Discriminator loss:

Ⅰdiscriminator loss

self.D_loss = discriminator_loss(self.D,self.input_y,self.G_y)

discriminator_loss只包括了adversarial loss.

discriminator_loss设计的adversarial loss很简单:

\hat{Q} 是真实点云, Q 是generator生成的fake点云, D(Q) 表示判别器输出的confidence value.

  1. def discriminator_loss(D, input_real, input_fake, Ra=False, gan_type='lsgan'):
  2. real = D(input_real)
  3. fake = D(input_fake)
  4. real_loss = tf.reduce_mean(tf.square(real - 1.0))
  5. fake_loss = tf.reduce_mean(tf.square(fake))
  6. loss = real_loss + fake_loss
  7. return loss

Ⅱ generator loss

  1. self.dis_loss = self.opts.fidelity_w * pc_distance(self.G_y, self.input_y, radius=self.pc_radius)
  2. if self.opts.use_repulse:
  3. self.repulsion_loss = self.opts.repulsion_w*get_repulsion_loss(self.G_y)
  4. else:
  5. self.repulsion_loss = 0
  6. self.uniform_loss = self.opts.uniform_w * get_uniform_loss(self.G_y)
  7. self.pu_loss = self.dis_loss + self.uniform_loss + self.repulsion_loss + tf.losses.get_regularization_loss()
  8. self.G_gan_loss = self.opts.gan_w*generator_loss(self.D,self.G_y)
  9. self.total_gen_loss = self.G_gan_loss + self.pu_loss

generator loss包括了reconstruction loss,repulsion loss,uniform loss和adversarial loss.

1)adversarial loss

 

与上面Discriminator的adversarial loss基本类似:

  1. def generator_loss(D,input_fake):
  2. fake = D(input_fake)
  3. fake_loss = tf.reduce_mean(tf.square(fake - 1.0))
  4. return fake_loss

2)reconstruction loss

PU-GAN默认使用的是EMD loss,EMD的详细理解见这篇文章:

刘昕宸:点云距离度量:完全解析EMD距离(Earth Mover's Distance)​zhuanlan.zhihu.com图标

3)repulsion loss

repulsion loss设计来自PU-Net,想详细了解可参考这篇文章:

刘昕宸:细嚼慢咽读论文:点云上采样网络开天辟地PU-Net​zhuanlan.zhihu.com图标

4)uniform loss

PU-GAN这一工作的一大贡献就是设计了uniform loss来控制生成点云分布的均匀性。

首先PU-Net设计了NUC这一评价指标来衡量生成点云的均匀性,但是这一评价忽视了local clutter of points,因此不宜再被采纳。

什么叫“忽视了local clutter of points”呢?

下面三个disk包含了相同数量的点(因此NUC都是一样的),但是它们的均匀程度显然是不同的。造成NUC衡量失效的原因,很大可能是局部点分布均匀程度NUC是无法刻画的。

而这里uniform loss的设计就是同时考虑了global和local!!!

第一项:

对于有 rN 个点的点集 Q (在实验中实际就是1个patch):

step 1. 使用最远点采样(FPS)采样 M 个seed points

step 2. 以每个seed point为球心,使用半径为 r_{d} 的ball query得到point subset S_{j}, j = 1...M .

分析:

严格坐落在 Q 表面面积为 \pi r_{d}^{2} 的local disk上。

还记得上面介绍的Ⅰ Patch Extraction嘛?我们通过测地线距离+正则化提取patch,因此patch就已经被我们normalize到一个unit sphere中了,patch的表面积是 \pi1 ^{2}

因此 S_{j} 内expected percentage of points p 就应该是 

并且 S_{j} 内expected number of points \hat{n} 就应该是 rNp 了

自然而然地,遵循chi-square model设计了uniform loss的第一项,用来衡量 \left | S_{j} \right | 与 \hat{n} 的偏差:

 

 

第二项:

考虑local point clutter,对 S_{j} 中的每个点,找到其最近邻并计算距离  d_{j,k}( k 表示第 S_{j} 中的第 k 个点)

 想象一下,如果 S_{j} 是均匀分布的,那么点与点之间的距离分布应该是这样的:

 

此时expected point-to-neighbor distance

  

自然而然地,再次遵循chi-square model,设计了uniform loss的第二项,用来衡量 ​​​​​​​d_{j,k}[公式] 与 \hat{d}[公式] 的偏差:

因此最终我们可得uniform loss:

程序实现:

  1. def get_uniform_loss(pcd, percentages=[0.004,0.006,0.008,0.010,0.012], radius=1.0):
  2. B,N,C = pcd.get_shape().as_list()
  3. npoint = int(N * 0.05)
  4. loss=[]
  5. for p in percentages:
  6. nsample = int(N*p)
  7. r = math.sqrt(p*radius)
  8. disk_area = math.pi *(radius ** 2) * p/nsample
  9. #print(npoint,nsample)
  10. new_xyz = gather_point(pcd, farthest_point_sample(npoint, pcd)) # (batch_size, npoint, 3)
  11. idx, pts_cnt = query_ball_point(r, nsample, pcd, new_xyz)#(batch_size, npoint, nsample)
  12. #expect_len = tf.sqrt(2*disk_area/1.732)#using hexagon
  13. expect_len = tf.sqrt(disk_area) # using square
  14. grouped_pcd = group_point(pcd, idx)
  15. grouped_pcd = tf.concat(tf.unstack(grouped_pcd, axis=1), axis=0)
  16. var, _ = knn_point(2, grouped_pcd, grouped_pcd)
  17. uniform_dis = -var[:, :, 1:]
  18. uniform_dis = tf.sqrt(tf.abs(uniform_dis+1e-8))
  19. uniform_dis = tf.reduce_mean(uniform_dis,axis=[-1])
  20. uniform_dis = tf.square(uniform_dis - expect_len) / (expect_len + 1e-8)
  21. uniform_dis = tf.reshape(uniform_dis, [-1])
  22. mean, variance = tf.nn.moments(uniform_dis, axes=0)
  23. mean = mean*math.pow(p*100,2)
  24. #nothing 4
  25. loss.append(mean)
  26. return tf.add_n(loss)/len(percentages)

4 dataset and experiments

4.1 dataset

PU-Net和MPU的数据集以及Visionair repository中挑选了147个模型,尽可能多地覆盖不同的类型。其中120个模型用于训练,27个用于测试。

训练数据的准备:因为PU-GAN是基于patch的,因此需要先对各个模型提取patch。在每个训练模型上提取200个patch120个模型就一共能提取24000个patch用于训练其中每个patch就是一个(input patch, groundtruth patch)的pairinput patch有256个点groundtruth patch有1024个点

4.2 metrics

评价指标包括了4类:

  1. point-to-surface (P2F) distance
  2. Chamfer distance (CD)
  3. Hausdorff distance (HD)
  4. uniform metrics: 评估方法类同上面的uniform loss

4.3 experiments

  1. 定量比较

2. 定性比较

3. 真实扫描场景的上采样

针对KITTI数据集:

4. 消融实验

5 conclusion

PU-GAN基本延续了PU-Net的思路,但是在诸多细节上均做了非常明显的改进(gridding, self-attention, dense connection, up-down-up, uniform loss等),也确实取得了更加强大的效果。

更值得关注的是,PU-GAN还在真实扫描点云数据集Kitti上做了实验,仍然取得了不错的效果,这一方面既证明了PU-GAN网络的泛化能力,另一方面也说明了PU-GAN具有非常大的实际使用价值,这一点是非常重要的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/582702
推荐阅读
相关标签
  

闽ICP备14008679号