pairwise_distance():计算输入点云每对点之间的欧式距离
(x1-x2)2+(y1-y2)2+(z1-z2)2
=x12+y12+z12+x22+y22+z22-2(x1x2+y1y2+z1z2)
pairwise_distance的返回值是一个形状为(B,N,N)的张量,也就是有B个batch,每个batch有N个点,而每个点又对应了全部点的pairwise_distance。
def pairwise_distance(point_cloud):
"""Compute pairwise distance of a point cloud.
Args:
point_cloud: tensor (batch_size, num_points, num_dims)
Returns:
pairwise distance: (batch_size, num_points, num_points)
"""
og_batch_size = point_cloud.get_shape().as_list()[0]
point_cloud = tf.squeeze(point_cloud) # 部分1
if og_batch_size == 1:
point_cloud = tf.expand_dims(point_cloud, 0)
point_cloud_transpose = tf.transpose(point_cloud, perm=[0, 2, 1]) # 部分2
point_cloud_inner = tf.matmul(point_cloud, point_cloud_transpose) # 部分2
point_cloud_inner = -2*point_cloud_inner # 部分2
point_cloud_square = tf.reduce_sum(tf.square(point_cloud), axis=-1, keep_dims=True) # 部分3
point_cloud_square_tranpose = tf.transpose(point_cloud_square, perm=[0, 2, 1]) # 部分3
return point_cloud_square + point_cloud_inner + point_cloud_square_tranpose # 部分4
部分1:输入点云
我们可以将输入的点云理解为一个瘦高灰度图像,即竖着的长条矩阵。矩阵的每一个行代表一个空间点,这一行里的值有坐标值(现在仅仅讨论这个最简单的情况)。
部分2:矩阵转置与矩阵乘法
现在,把这个矩阵copy一份,并且给它放躺下,作矩阵乘法(如下图)。形状为(B,N,3)转置为(B,3,N),然后二者矩阵乘法得到形状为(B,N,N)的张量,叫它point_cloud_inner。现在我们来看看这个结果到底是什么。
point_cloud_inner的几何意义就是,开头公式中的(x1x2+y1y2+z1z2)
现在要把这个point_cloud_inner的每个元素乘以-2
也就变成了开头公式中的-2(x1x2+y1y2+z1z2),现在还差啥?接着凑x12+y12+z12+x22+y22+z22
部分3:输入点云各自平方再求和
对输入点云的每一行元素各自求取平方,然后再加起来,得到形状为(B,N,1)的张量。这里每个点看作一个空间向量,求自身的模的平方,也就是自己长度的平方,叫这个张量为point_cloud_square。这个就是x2+y2+z^2,但是,这个是将点坐标平方和竖着放的,也就是一行对应一个点,也就是这个点的坐标平方和。
之后,point_cloud_square经过转置变为形状为(B,1,N),叫这个结果为point_cloud_square_tranpose。这个就有意思了,这个是横着放的,一个列对应一个点,也就是一个列放着不同的点坐标平方和。
部分4:矩阵元素求和
这一部分是最后一部分,求取point_cloud_square、 point_cloud_inner 、point_cloud_square_tranpose的代数和,也是最终的返回值。这里面用到了广播机制,下面图里面的先复制,再相加。也就是说当形状为(B,N,N)与(B,N,1)相加时,后者沿着为1的维度复制为(B,N,N),然后在于前者相加。point_cloud_square与point_cloud_square_tranpose都会被广播为(B,N,N)然后在与 point_cloud_inner进行相加。
point_cloud_square是竖着放点坐标和,所以横着广播,变成了
a a a …
b b b…
…
i i i…
…
point_cloud_square是横着放,所以竖着广播,变成了
a b c … i …
a b c … i …
…
a b c … i …
…
然后point_cloud_square+point_cloud_square不就实现了x12+y12+z12+x22+y22+z22吗?
point_cloud_square+point_cloud_inner+point_cloud_square不就为每个点凑齐了x12+y12+z12+x22+y22+z22-2(x1x2+y1y2+z1z2)吗?
这个就是点间的距离。
最后的返回张量形状为(B,N,N),取其中(b,i,j),那么其具体含义就是,第b个batch中,第i个点与第j个点之间的欧式距离。倘若输入点云除了坐标还有属性,则这个向量是一个高维空间(特征空间)的向量。
(4条消息) tf.nn.top_k()用法_太空的旅行者的博客-CSDN博客
KNN
如果两个点的距离越远,那么这个pairwise_distance张量的对应位置所存储的值就越大,反之亦然。同时,knn是找点的最近的k个点。
def knn(adj_matrix, k=20):
"""Get KNN based on the pairwise distance.
Args:
pairwise distance: (batch_size, num_points, num_points)
k: int
Returns:
nearest neighbors: (batch_size, num_points, k)
"""
neg_adj = -adj_matrix
_, nn_idx = tf.nn.top_k(neg_adj, k=k)
return nn_idx
主要利用tf.nn.top_k()来查找最近的点。因为tf.nn.top_k()只能查找最大的元素,所以把输入的adj_matrix也就是pairwise_distance的返回值乘以-1,这样在通过tf.nn.top_k()返回的就是最小值了。
tf.nn.top_k()沿着最后一个维度搜索,先返回值,再返回值的索引。这里索引是我们需要的。
get_edge_feature()——边特征的学习和理解
(4条消息) 点云dgcnn边特征理解_太空的旅行者的博客-CSDN博客