def local_map_loss(self, temp_points, deform_points, K=8, D=1):
        if D == 1:
            _, indices = pf.knn_indices_general(temp_points, temp_points, K,
                                                False)
        else:
            _, indices_dilated = pf.knn_indices_general(
                temp_points, temp_points, K * D, True)
            indices = indices_dilated[:, :, ::D, :]
        batch_size = deform_points.get_shape()[0].value
        points_num = deform_points.get_shape()[1].value

        offsets = tf.subtract(deform_points, temp_points)
        dists = tf.sqrt(tf.reduce_sum(tf.pow(offsets, 2), axis=-1))
        directions = offsets / tf.tile(tf.expand_dims(dists, axis=-1),
                                       [1, 1, 3])
        nn_directions = tf.gather_nd(directions, indices,
                                     name='nn_pts')  # batch*point*k*3
        nn_cosin_matrix = 1 - tf.matmul(
            nn_directions, nn_directions, transpose_b=True)
        nn_cosin_matrix = tf.reshape(nn_cosin_matrix,
                                     shape=[batch_size, points_num, K * K])
        loss = tf.reduce_mean(nn_cosin_matrix, axis=[0, 1, 2])
        loss = loss + 0.1 * tf.reduce_mean(dists, axis=[0, 1])

        return loss
Exemple #2
0
def points_laplace_coord(points, K=8, D=1):
    if D == 1:
        _, indices = pf.knn_indices_general(points, points, K, False)
    else:
        _, indices_dilated = pf.knn_indices_general(points, points, K*D, True)
        indices = indices_dilated[:, :, ::D, :]
    # calculate laplacian coordinate:
    nn_points = tf.gather_nd(points, indices, name = 'nn_pts')
    points_tile = tf.expand_dims(points, axis=2, name = 'points_expand')
    points_tile = tf.tile(points_tile, [1, 1, K, 1])
    mean_coords = tf.reduce_mean(tf.subtract(points_tile, nn_points), axis=-2)
    lap_coords = tf.subtract(points, mean_coords)

    return lap_coords
Exemple #3
0
def shellconv(pts, fts_prev, qrs, is_training, tag, K, D, P, C, with_local, bn_decay=None):
    indices = pf.knn_indices_general(qrs, pts, K, True)

    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  # (N, P, K, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local')  # (N, P, K, 3)

    [N,P,K,dim] = nn_pts_local.shape  # (N, P, K, 3)
    nn_fts_local = None
    C_pts_fts = 64
    if with_local:
        nn_fts_local = dense(nn_pts_local, C_pts_fts // 2, is_training, tag + 'nn_fts_from_pts_0', bn_decay=bn_decay)
        nn_fts_local = dense(nn_fts_local, C_pts_fts, is_training, tag + 'nn_fts_from_pts', bn_decay=bn_decay)
    else:
        nn_fts_local = nn_pts_local

    if fts_prev is not None:
        fts_prev = tf.gather_nd(fts_prev, indices, name=tag + 'fts_prev')  # (N, P, K, 3)
        pts_X_0 = tf.concat([nn_fts_local,fts_prev], axis=-1)
    else:
        pts_X_0 = nn_fts_local

    s = int(K.value/D)  # no. of divisions
    feat_max = tf.layers.max_pooling2d(pts_X_0, [1,s], strides=[1,s], padding='valid', name=tag+'maxpool_0')

    fts_X = conv2d(feat_max, C, name=tag+'conv', is_training=is_training, kernel_size=[1,feat_max.shape[-2].value])

    fts_X = tf.squeeze(fts_X, axis=-2)
    return fts_X
Exemple #4
0
def xconv(pts, fts, qrs, tag, N, K, D, P, C, C_pts_fts, is_training, with_X_transformation, depth_multiplier,
          sorting_method=None, with_global=False):
    if D == 1:
        _, indices = pf.knn_indices_general(qrs, pts, K, True)
    else:
        _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
        indices = indices_dilated[:, :, ::D, :]

    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)

    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  # (N, P, K, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local')  # (N, P, K, 3)

    # Prepare features to be transformed
    nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts, tag + 'nn_fts_from_pts_0', is_training)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts, tag + 'nn_fts_from_pts', is_training)
    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts, indices, name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev], axis=-1, name=tag + 'nn_fts_input')

    if with_X_transformation:
        ######################## X-transformation #########################
        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training, (1, K))
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training, (1, K))
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        X_2 = pf.depthwise_conv2d(X_1_KK, K, tag + 'X_2', is_training, (1, K), activation=None)
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')
        ###################################################################
    else:
        fts_X = nn_fts_input

    fts_conv = pf.separable_conv2d(fts_X, C, tag + 'fts_conv', is_training, (1, K), depth_multiplier=depth_multiplier)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')

    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global_', is_training)
        return tf.concat([fts_global, fts_conv_3d], axis=-1, name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
Exemple #5
0
def point_conv(pts,
               fts,
               qrs,
               K,
               D,
               C,
               is_training,
               activation=tf.nn.relu,
               bn=True,
               sorting_method='cxyz',
               center_patch=True,
               tag='point_conv'):
    '''
    pts: xyz points, Tensor, (batch_size, npoint, 3)
    fts: features, Tensor, (batch_size, npoint, channel_fts)
    qrs: xyz points as queries, Tensor, (batch_size, n_qrs, 3)
    K: number of neighbors for one query, scalar, int
    D: dilation rate
    C: output feature channel number
    '''
    with tf.variable_scope(tag):
        _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
        indices = indices_dilated[:, :, ::D, :]

        if sorting_method is not None:
            indices = pf.sort_points(pts, indices, sorting_method)

        nn_pts = tf.gather_nd(pts, indices, name='nn_pts')  # (N, P, K, 3)
        if center_patch:
            nn_pts_center = tf.expand_dims(
                qrs, axis=2, name='nn_pts_center')  # (N, P, 1, 3)
            nn_pts_local = tf.subtract(
                nn_pts, nn_pts_center,
                name='nn_pts_local')  # (N, P, K, 3), move to local frame
        else:
            nn_pts_local = nn_pts

        if fts is None:
            nn_fts_input = nn_pts_local
        else:
            nn_fts_from_prev = tf.gather_nd(fts, indices, 'nn_fts_from_prev')
            nn_fts_input = tf.concat(
                [nn_pts_local, nn_fts_from_prev], axis=-1,
                name='nn_fts_input')  # (N, P, K, 3+C_prev_fts)

        fts_conv = pf.conv2d(nn_fts_input,
                             C,
                             'fts_conv',
                             is_training, (1, K),
                             with_bn=bn,
                             activation=activation)  # (N, P, 1, C)
        fts_conv_3d = tf.squeeze(fts_conv, axis=2,
                                 name='fts_conv_3d')  # (N, P, C)

        return fts_conv_3d, indices
def sa_conv(pts, fts, qrs, K, D, mlp, bn, is_training, bn_decay, scope):
    """
    pts: points of previous layer,
    fts: point features of previous layer,
    qrs: selected representative points of this layer,
    N: batch_size,
    K: neighbor_size,
    D: dilation parameter,
    P: the number of selected representative points,
    C: output feature number per point,
    C_pts_fts: feature number for each local point
    """
    if D == 1:
        _, indices = pf.knn_indices_general(qrs, pts, K, True)
    else:
        _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
        indices = indices_dilated[:, :, ::D, :]

    new_points = tf.gather_nd(fts, indices)  # (N, P, K, 3)
    with tf.variable_scope(scope) as sc:
        for i, num_out_channel in enumerate(mlp):
            new_points = tf_util.conv2d(new_points,
                                        num_out_channel, [1, 1],
                                        padding='VALID',
                                        stride=[1, 1],
                                        bn=bn,
                                        is_training=is_training,
                                        scope='conv%d' % (i),
                                        bn_decay=bn_decay)
        new_points = tf.reduce_max(new_points,
                                   axis=[2],
                                   keep_dims=True,
                                   name='maxpool')
        fts_conv_3d = tf.squeeze(new_points,
                                 [2])  # (batch_size, npoints, mlp[-1])

        return fts_conv_3d
Exemple #7
0
def shellconv(pts, fts_prev, qrs, is_training, tag, K, D, P, C, with_local, bn_decay=None, norm='l2'):
    indices = pf.knn_indices_general(qrs, pts, K, True, norm=norm)
    if norm == 'l1':
        # Adjust the max distance so that the hypercube is of the same volume
        # as the hyperball, for fairness.
        #
        # Oh, wait, shell size D is the number of points, not the max distance...
        # D = int(D * np.sqrt(3.141592) / np.exp(1.0 / C * sp.special.loggamma(0.5 * C + 1)))
        pass

    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  # (N, P, K, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local')  # (N, P, K, 3)

    [N,P,K,dim] = nn_pts_local.shape # (N, P, K, 3)
    nn_fts_local = None
    C_pts_fts = 64
    if with_local:
        nn_fts_local = dense(nn_pts_local, C_pts_fts // 2, is_training, tag + 'nn_fts_from_pts_0',bn_decay=bn_decay)
        nn_fts_local = dense(nn_fts_local, C_pts_fts, is_training, tag + 'nn_fts_from_pts',bn_decay=bn_decay)
    else:
        nn_fts_local = nn_pts_local

    if fts_prev is not None:
        fts_prev = tf.gather_nd(fts_prev, indices, name=tag + 'fts_prev')  # (N, P, K, 3)
        pts_X_0 = tf.concat([nn_fts_local,fts_prev], axis=-1)
    else:
        pts_X_0 = nn_fts_local

    s = int(K.value/D)  # no. of divisions
    feat_max = tf.layers.max_pooling2d(pts_X_0, [1,s], strides=[1,s], padding='valid', name=tag+'maxpool_0')

    fts_X = conv2d(feat_max, C, name=tag+'conv', is_training=is_training, kernel_size=[1,feat_max.shape[-2].value])

    fts_X = tf.squeeze(fts_X, axis=-2)
    return fts_X
def xconv(pts,
          fts,
          qrs,
          tag,
          N,
          K,
          D,
          P,
          C,
          depth_multiplier,
          is_training,
          C_pts_fts,
          with_x_transformation=True,
          sorting_method=None,
          with_global=False):
    ###xconv(pts,fts,qrs,tag,N,K,D,P,C,C_pts_fts,is_training,with_X_transformation,depth_multipiler,sorting_method,with_global)

    # @params:
    #     N: Number of output points
    #     K: Number of nearest neighbot
    #     D: dilation rate
    #     C: Number of output channels
    #     P:the representative point number in the output, -1 means all input points are output representative points
    #     x_transformation: replace max_pooling in PointNet
    #     sorting_method:
    #     with_global
    #     pts:Input point cloud
    #     qrs:queries
    #     fts:features

    _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)

    indices = indices_dilated[:, :, ::D, :]
    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)

    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  #(N,P,K,3)
    nn_pts_center = tf.expand_dims(qrs, axis=2,
                                   name=tag + 'nn_pts_center')  #(N,P,1,3)
    nn_pts_local = tf.subtract(nn_pts,
                               nn_pts_center,
                               name=tag + 'nn_pts_local')  #(N,P,K,3)

    #Prepare features to be transformed

    nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                 tag + 'nn_fts_from_pts_0', is_training)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                               tag + 'nn_fts_from_pts', is_training)

    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts,
                                        indices,
                                        name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev],
                                 axis=-1,
                                 name=tag + 'nn_fts_input')

    #X_transformation
    if with_x_transformation:
        ############################X_transformation#######################################################

        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training, (1, K))
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')

        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training, (1, K))
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')

        X_2 = pf.depthwise_conv2d(X_1_KK,
                                  K,
                                  tag + 'X_2',
                                  is_training, (1, K),
                                  activation=None)
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')

        #####################################################################################################

    else:
        fts_X = nn_fts_input

    fts_conv = pf.separable_conv2d(fts_X,
                                   C,
                                   tag + 'fts_conv',
                                   is_training, (1, K),
                                   depth_multiplier=depth_multiplier)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')

    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global',
                              is_training)
        return tf.concat([fts_global, fts_conv_3d],
                         axis=-1,
                         name=tag + 'fts_conv_3d_with_global')

    else:
        return fts_conv_3d
Exemple #9
0
def RIConv(pts, fts_prev, qrs, is_training, tag, K, D, P, C, with_local, bn_decay=None):

    indices = pf.knn_indices_general(qrs, pts, int(K), True)
    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')
    
    nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')
    nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local') 
    dists_local = tf.norm(nn_pts_local, axis=-1, keepdims=True)  # dist to center

    mean_local = tf.reduce_mean(nn_pts, axis=-2, keepdims=True)
    mean_global = tf.reduce_mean(pts, axis=-2, keepdims=True)
    mean_global = tf.expand_dims(mean_global, axis=-2)

    nn_pts_local_mean = tf.subtract(nn_pts, mean_local, name=tag + 'nn_pts_local_mean') 
    dists_local_mean = tf.norm(nn_pts_local_mean, axis=-1, keepdims=True) # dist to local mean

    vec = mean_local - nn_pts_center
    vec_dist = tf.norm(vec, axis=-1, keepdims =True)
    vec_norm = tf.divide(vec, vec_dist)
    vec_norm = tf.where(tf.is_nan(vec_norm), tf.ones_like(vec_norm) * 0, vec_norm) 

    nn_pts_local_proj = tf.matmul(nn_pts_local, vec_norm, transpose_b=True)
    nn_pts_local_proj_dot = tf.divide(nn_pts_local_proj, dists_local)
    nn_pts_local_proj_dot = tf.where(tf.is_nan(nn_pts_local_proj_dot), tf.ones_like(nn_pts_local_proj_dot) * 0, nn_pts_local_proj_dot)  # check nan

    nn_pts_local_proj_2 = tf.matmul(nn_pts_local_mean, vec_norm, transpose_b=True)
    nn_pts_local_proj_dot_2 = tf.divide(nn_pts_local_proj_2, dists_local_mean)
    nn_pts_local_proj_dot_2 = tf.where(tf.is_nan(nn_pts_local_proj_dot_2), tf.ones_like(nn_pts_local_proj_dot_2) * 0, nn_pts_local_proj_dot_2)  # check nan

    nn_fts = tf.concat([dists_local, dists_local_mean, nn_pts_local_proj_dot, nn_pts_local_proj_dot_2], axis=-1) # d0 d1 a0 a1
    
    # compute indices from nn_pts_local_proj
    vec = mean_global - nn_pts_center
    vec_dist = tf.norm(vec, axis=-1, keepdims =True)
    vec_norm = tf.divide(vec, vec_dist)
    nn_pts_local_proj = tf.matmul(nn_pts_local, vec_norm, transpose_b=True)

    proj_min = tf.reduce_min(nn_pts_local_proj, axis=-2, keepdims=True) 
    proj_max = tf.reduce_max(nn_pts_local_proj, axis=-2, keepdims=True) 
    seg = (proj_max - proj_min) / D

    vec_tmp = tf.range(0, D, 1, dtype=tf.float32)
    vec_tmp = tf.reshape(vec_tmp, (1,1,1,D))

    limit_bottom = vec_tmp * seg + proj_min
    limit_up = limit_bottom + seg

    idx_up = nn_pts_local_proj <= limit_up
    idx_bottom = nn_pts_local_proj >= limit_bottom
    idx = tf.to_float(tf.equal(idx_bottom, idx_up))
    idx_expand = tf.expand_dims(idx, axis=-1)

    
    
    [N,P,K,dim] = nn_fts.shape # (N, P, K, 3)
    nn_fts_local = None
    if with_local:
        C_pts_fts = 64
        nn_fts_local_reshape = tf.reshape(nn_fts, (-1,P*K,dim,1))
        nn_fts_local = tf_util.conv2d(nn_fts_local_reshape, C_pts_fts//2, [1,dim],
                         padding='VALID', stride=[1,1], 
                         bn=True, is_training=is_training,
                         scope=tag+'conv_pts_fts_0', bn_decay=bn_decay)
        nn_fts_local = tf_util.conv2d(nn_fts_local, C_pts_fts, [1,1],
                         padding='VALID', stride=[1,1], 
                         bn=True, is_training=is_training,
                         scope=tag+'conv_pts_fts_1', bn_decay=bn_decay)
        nn_fts_local = tf.reshape(nn_fts_local, (-1,P,K,C_pts_fts))
    else:
        nn_fts_local = nn_fts
    
    if fts_prev is not None:
        fts_prev = tf.gather_nd(fts_prev, indices, name=tag + 'fts_prev')  # (N, P, K, 3)
        pts_X_0 = tf.concat([nn_fts_local,fts_prev], axis=-1)
    else:
        pts_X_0 = nn_fts_local

    pts_X_0_expand = tf.expand_dims(pts_X_0, axis=-2)
    nn_fts_rect = pts_X_0_expand * idx_expand
    idx = tf.to_float(nn_fts_rect == 0.0)
    nn_fts_rect = nn_fts_rect + idx*(-99999999999.0)
    nn_fts_rect = tf.reduce_max(nn_fts_rect, axis=-3)
    
    # nn_fts_rect = tf.matmul(idx_mean, pts_X_0, transpose_a = True)
        
    fts_X = tf_util.conv2d(nn_fts_rect, C, [1,nn_fts_rect.shape[-2].value],
                         padding='VALID', stride=[1,1], 
                         bn=True, is_training=is_training,
                         scope=tag+'conv', bn_decay=bn_decay)
    return tf.squeeze(fts_X, axis=-2) 
Exemple #10
0
def xconv(pts,
          fts,
          qrs,
          tag,
          N,
          K,
          D,
          P,
          C,
          C_pts_fts,
          is_training,
          with_X_transformation,
          depth_multiplier,
          sorting_method=None,
          with_global=False):
    _, indices_dilated = pf.knn_indices_general(
        qrs, pts, K * D, True
    )  # 带孔K*D近邻; (N, P, 3)  +  (N, P, 3)  => (N, P, K*D, 2) N=batch_num; P=point_num; 最后一维两个数:batch_indice + point_indice
    indices = indices_dilated[:, :, ::
                              D, :]  # (N, P, K, 2) 隔D取样,增加receptive field

    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)

    nn_pts = tf.gather_nd(
        pts, indices, name=tag + 'nn_pts'
    )  # (N, P, 3) + (N, P, K, 2) => (N, P, K, 3) indices维度比pts高,最后一维做索引,batch_indice->N, point_indice->P, 最后剩下(N, P, 3)里的3是坐标,补进去返回形状是 (N, P, K, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2,
                                   name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(
        nn_pts, nn_pts_center,
        name=tag + 'nn_pts_local')  # (N, P, K, 3),减去中心点的坐标系,得到局部坐标系

    # Prepare features to be transformed
    # 2个全连接层 对点的相对坐标系升维,(N, P, K, 3) => (N, P, K, C_pts_fts)
    # 升维的目的是为了和前面的特征拼接 (ct.)
    nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                 tag + 'nn_fts_from_pts_0', is_training)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                               tag + 'nn_fts_from_pts', is_training)
    # 如果第一次没有feature,就只使用从局部得到的点特征,如果有之前的特征,就拼接二者(ct.)
    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts,
                                        indices,
                                        name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev],
                                 axis=-1,
                                 name=tag + 'nn_fts_input')  # 拼接坐标特征和之前的特征

    if with_X_transformation:
        ######################## X-transformation #########################
        # (N, P, K, 3)  > (N, P, 1, K * K)  ; kernel size 是(1, K)
        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training, (1, K))
        # (N, P, 1, K * K) > (N, P, K, K)
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        # (N, P, K, K) > (N, P, 1, K * K)  可能为了较少参数量用depthwise_conv2d换conv2d
        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training, (1, K))
        # (N, P, 1, K * K) > (N, P, K, K)
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        # (N, P, K, K) > (N, P, 1, K * K)
        X_2 = pf.depthwise_conv2d(X_1_KK,
                                  K,
                                  tag + 'X_2',
                                  is_training, (1, K),
                                  activation=None)
        # (N, P, 1, K * K) > (N, P, K, K)  最后的x矩阵 (K*K)
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        # 用得到的 x 矩阵乘以之前的特征,所以这里其实是有2个分支,一个计算x矩阵,另外一个计算特征;(ct.)
        # (N, P, K, K) * (N, P, K, C_pts_fts) = (N, P, K, C_pts_fts)
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')
        ###################################################################
    else:
        fts_X = nn_fts_input

    # 最后的分离卷积
    fts_conv = pf.separable_conv2d(fts_X,
                                   C,
                                   tag + 'fts_conv',
                                   is_training, (1, K),
                                   depth_multiplier=depth_multiplier
                                   )  # (N, P, K, C_pts_fts) -> (N, P, 1, C)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2,
                             name=tag + 'fts_conv_3d')  # (N, P, C)

    # 用代表点全局位置信息,
    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        fts_global = pf.dense(
            fts_global_0, C // 4, tag + 'fts_global', is_training
        )  # (N, P, C//4)  最后一层时作者使receptive field < 1让model更会捕捉local feature; 从“subvolume supervision”得到启发 to further address the over-fitting problem.;
        return tf.concat([fts_global, fts_conv_3d],
                         axis=-1,
                         name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
def xconv_cov(cov,
              pts,
              fts,
              qrs,
              tag,
              N,
              K,
              D,
              P,
              C,
              C_pts_fts,
              is_training,
              with_X_transformation,
              depth_multiplier,
              sorting_method=None,
              with_global=False):
    """
    cov: point covariance (B, N, 9) of previous layer
    pts: points of previous layer,
    fts: point features of previous layer,
    qrs: selected representative points of this layer,
    N: batch_size,
    K: neighbor_size,
    D: dilation parameter,
    P: the number of selected representative points,
    C: output feature number per point,
    C_pts_fts: feature number for each local point
    """
    if D == 1:
        _, indices = pf.knn_indices_general(qrs, pts, K, True)
    else:
        _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
        indices = indices_dilated[:, :, ::D, :]

    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)

    nn_covs = tf.gather_nd(cov, indices, name=tag + 'nn_pts')  # (N, P, K, 9)
    # nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    # nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local')  # (N, P, K, 3)

    # Prepare features to be transformed
    nn_fts_from_pts_0 = pf.dense(nn_covs,
                                 C_pts_fts,
                                 tag + 'nn_fts_from_pts_0',
                                 is_training,
                                 with_bn=False)  # following paper
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0,
                               C_pts_fts,
                               tag + 'nn_fts_from_pts',
                               is_training,
                               with_bn=False)  # following paper
    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts,
                                        indices,
                                        name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev],
                                 axis=-1,
                                 name=tag + 'nn_fts_input')

    if with_X_transformation:
        ######################## X-transformation #########################
        X_0 = pf.conv2d(nn_covs,
                        K * K,
                        tag + 'X_0',
                        is_training, (1, K),
                        with_bn=False)  # following paper
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        X_1 = pf.depthwise_conv2d(X_0_KK,
                                  K,
                                  tag + 'X_1',
                                  is_training, (1, K),
                                  with_bn=False)  # following paper
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        X_2 = pf.depthwise_conv2d(X_1_KK,
                                  K,
                                  tag + 'X_2',
                                  is_training, (1, K),
                                  with_bn=False,
                                  activation=None)  # following paper
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')
        ###################################################################
    else:
        fts_X = nn_fts_input

    fts_conv = pf.separable_conv2d(fts_X,
                                   C,
                                   tag + 'fts_conv',
                                   is_training, (1, K),
                                   depth_multiplier=depth_multiplier,
                                   with_bn=True)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')

    if with_global:
        fts_global_0 = pf.dense(qrs,
                                C // 4,
                                tag + 'fts_global_0',
                                is_training,
                                with_bn=True)
        fts_global = pf.dense(fts_global_0,
                              C // 4,
                              tag + 'fts_global_',
                              is_training,
                              with_bn=True)
        return tf.concat([fts_global, fts_conv_3d],
                         axis=-1,
                         name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
def deconv_new(pts,
               fts,
               qrs,
               tag,
               N,
               K,
               radius,
               P,
               C,
               C_pts_fts,
               is_training,
               with_X_transformation,
               depth_multiplier,
               D=1,
               sorting_method=None,
               with_global=False,
               knn=False):
    """
    pts: points of previous layer, e.g.,(B, N/2, 3)
    fts: point features of previous layer, e.g.,(B, N/2, C)
    qrs: selected representative points of this layer, e.g.,(B, N, 3)
    N: batch_size,
    K: neighbor_size,
    D: dilation parameter,
    P: the number of selected representative points,
    C: output feature number per point,
    C_pts_fts: feature number for each local point
    radius: float32, the radius of query ball search
    knn: True: knn; False: query ball
    """
    dist, idx = three_nn(qrs, pts)
    dist = tf.maximum(dist, 1e-10)
    norm = tf.reduce_sum((1.0 / dist), axis=2, keep_dims=True)
    norm = tf.tile(norm, [1, 1, 3])
    weight = (1.0 / dist) / norm
    interpolated_fts = three_interpolate(fts, idx, weight)  # (B, N, C)

    if knn:
        _, indices_dilated = pf.knn_indices_general(qrs, qrs, K * D, True)
        indices = indices_dilated[:, :, ::D, :]
        nn_pts = tf.gather_nd(qrs, indices,
                              name=tag + 'nn_pts')  # (B, N, K, 3)
        nn_fts_from_prev = tf.gather_nd(interpolated_fts,
                                        indices,
                                        name=tag + 'nn_fts')
    else:
        indices, pts_cnt = query_ball_point(radius, K, qrs, qrs)
        nn_pts = group_point(qrs, indices)
        nn_fts_from_prev = group_point(interpolated_fts, indices)

    nn_pts_center = tf.expand_dims(qrs, axis=2,
                                   name=tag + 'nn_pts_center')  # (B, N, 1, 3)
    nn_pts_local = tf.subtract(nn_pts,
                               nn_pts_center,
                               name=tag + 'nn_pts_local')  # (B, N, K, 3)

    # Prepare features to be transformed
    nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                 tag + 'nn_fts_from_pts_0', is_training)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                               tag + 'nn_fts_from_pts', is_training)

    nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev],
                             axis=-1,
                             name=tag + 'nn_fts_input')

    if with_X_transformation:
        ######################## X-transformation #########################
        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training,
                        (1, K))  # following paper
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training,
                                  (1, K))  # following paper
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        X_2 = pf.depthwise_conv2d(X_1_KK,
                                  K,
                                  tag + 'X_2',
                                  is_training, (1, K),
                                  activation=None)  # following paper
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')
        ###################################################################
    else:
        fts_X = nn_fts_input

    fts_conv = pf.separable_conv2d(fts_X,
                                   C,
                                   tag + 'fts_conv',
                                   is_training, (1, K),
                                   depth_multiplier=depth_multiplier)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')

    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global_',
                              is_training)
        return tf.concat([fts_global, fts_conv_3d],
                         axis=-1,
                         name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
Exemple #13
0
def point_conv_transpose(pts_in,
                         fts_in,
                         pts_out,
                         fts_out_skipped,
                         indices,
                         K,
                         D,
                         C,
                         is_training,
                         activation=tf.nn.relu,
                         bn=True,
                         sorting_method='cxyz',
                         center_patch=True,
                         tconv='fc',
                         tag='point_deconv'):
    '''
    pts_in: xyz points, Tensor, (batch_size, npoint, 3)
    fts_in: features, Tensor, (batch_size, npoint, channel_fts)
    pts_out: xyz points, Tensor, (batch_size, n_pts_out, 3), NOTE: pts_out is denser than pts.
    K: number of neighbors for one query, scalar, int
    D: dilation rate
    C: output feature channel number
    tconv: fc or t_conv
    in the comments:
        N - batch size
        P - number of pts_in
        K - number of neighbors
    '''
    with tf.variable_scope(tag):
        batch_size = pts_out.shape[0]
        n_pts_out = pts_out.shape[1]
        n_pts_in = pts_in.shape[1]

        if indices is None:
            _, indices_dilated = pf.knn_indices_general(
                pts_in, pts_out, K * D, True)
            indices = indices_dilated[:, :, ::D, :]

            if sorting_method is not None:
                indices = pf.sort_points(pts_out, indices,
                                         sorting_method)  # (N, P, K, 2)

        # move the gathered pts_out to local frame or not
        nn_pts_out = tf.gather_nd(pts_out, indices,
                                  name='nn_pts_out')  # (N, P, K, 3)
        if center_patch:
            nn_pts_out_center = tf.expand_dims(
                pts_in, axis=2, name='nn_pts_out_center')  # (N, P, 1, 3)
            nn_pts_out_local = tf.subtract(
                nn_pts_out, nn_pts_out_center,
                name='nn_pts_out_local')  # (N, P, K, 3)
        else:
            nn_pts_out_local = nn_pts_out

        if fts_in is None:
            nn_fts_input = pts_in  # (N, P, 3)
        else:
            # NOTE: do not concat pts_in with fts_in now, since fts_in already contains information of pts_in
            nn_fts_input = fts_in  # (N, P, C_fts_in)

        nn_fts_input = tf.expand_dims(nn_fts_input,
                                      axis=2)  # (N, P, 1, C_fts_in)

        # using fc
        if tconv == 'fc':
            fts_fc = pf.dense(nn_fts_input, K * C, 'fc_fts_prop',
                              is_training)  # (N, P, 1, K*C)
            fts_fc = tf.reshape(fts_fc,
                                (batch_size, n_pts_in, K, C))  # (N, P, K, C)
        elif tconv == 't_conv':
            fts_fc = pf.conv2d_transpose(nn_fts_input,
                                         C,
                                         tag + '_fts_deconv',
                                         is_training,
                                         kernel_size=(1, K),
                                         strides=(1, 1),
                                         with_bn=bn,
                                         activation=activation)  # (N, P, K, C)

        if fts_out_skipped is not None:
            nn_fts_out_skipped = tf.gather_nd(fts_out_skipped,
                                              indices,
                                              name='nn_fts_out_from_skip')
            fts_all_concat = tf.concat(
                [fts_fc, nn_fts_out_skipped, nn_pts_out_local],
                axis=-1)  # (N, P, K, C+3+channel_skipped)
        else:
            fts_all_concat = tf.concat([fts_fc, nn_pts_out_local],
                                       axis=-1)  # (N, P, K, C+3)

        fts_pts_conv = pf.conv2d(
            fts_all_concat,
            C,
            'fts_pts_conv1D',
            is_training,
            kernel_size=(1, 1),
            strides=(1, 1),
            with_bn=bn,
            activation=activation)  # (N, P, K, C+3+[ch_skip]) -> (N, P, K, C)

        # summing up feature for each point
        fts_dense = tf.scatter_nd(
            tf.reshape(indices, (-1, 2)), tf.reshape(fts_pts_conv, (-1, C)),
            (batch_size, n_pts_out, C))  # (N, N_PTS_OUT, c)

        return fts_dense
Exemple #14
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--filelist',
                        '-t',
                        help='Path to training set ground truth (.txt)',
                        required=True)
    parser.add_argument('--filelist_val',
                        '-v',
                        help='Path to validation set ground truth (.txt)',
                        required=True)
    parser.add_argument('--filelist_unseen',
                        '-u',
                        help='Path to validation set ground truth (.txt)',
                        required=True)
    parser.add_argument('--load_ckpt',
                        '-l',
                        help='Path to a check point file for load')
    parser.add_argument(
        '--save_folder',
        '-s',
        help='Path to folder for saving check points and summary',
        required=True)
    parser.add_argument('--model', '-m', help='Model to use', required=True)
    parser.add_argument('--setting',
                        '-x',
                        help='Setting to use',
                        required=True)
    args = parser.parse_args()

    time_string = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
    root_folder = os.path.join(
        args.save_folder,
        '%s_%s_%s_%d' % (args.model, args.setting, time_string, os.getpid()))
    if not os.path.exists(root_folder):
        os.makedirs(root_folder)

    print('PID:', os.getpid())

    print(args)

    model = importlib.import_module(args.model)
    setting_path = os.path.join(os.path.dirname(__file__), args.model)
    sys.path.append(setting_path)
    setting = importlib.import_module(args.setting)

    num_epochs = setting.num_epochs
    batch_size = setting.batch_size
    sample_num = setting.sample_num
    step_val = setting.step_val
    label_weights_list = setting.label_weights
    label_weights_val = [1.0] * 1 + [1.0] * (setting.num_class - 1)

    rotation_range = setting.rotation_range
    rotation_range_val = setting.rotation_range_val
    scaling_range = setting.scaling_range
    scaling_range_val = setting.scaling_range_val
    jitter = setting.jitter
    jitter_val = setting.jitter_val

    # Prepare inputs
    print('{}-Preparing datasets...'.format(datetime.now()))
    is_list_of_h5_list = not data_utils.is_h5_list(args.filelist)
    if is_list_of_h5_list:
        seg_list = data_utils.load_seg_list(args.filelist)
        seg_list_idx = 0
        filelist_train = seg_list[seg_list_idx]
        seg_list_idx = seg_list_idx + 1
    else:
        filelist_train = args.filelist
    data_train, _, data_num_train, label_train, _ = data_utils.load_seg(
        filelist_train)
    data_val, _, data_num_val, label_val, _ = data_utils.load_seg(
        args.filelist_val)

    data_unseen, _, data_num_unseen, label_unseen, _ = data_utils.load_seg(
        args.filelist_unseen)

    # shuffle
    data_unseen, data_num_unseen, label_unseen = \
        data_utils.grouped_shuffle([data_unseen, data_num_unseen, label_unseen])

    # shuffle
    data_train, data_num_train, label_train = \
        data_utils.grouped_shuffle([data_train, data_num_train, label_train])

    num_train = data_train.shape[0]
    point_num = data_train.shape[1]
    num_val = data_val.shape[0]
    num_unseen = data_unseen.shape[0]

    batch_num_unseen = int(math.ceil(num_unseen / batch_size))

    print('{}-{:d}/{:d} training/validation samples.'.format(
        datetime.now(), num_train, num_val))
    batch_num = (num_train * num_epochs + batch_size - 1) // batch_size
    print('{}-{:d} training batches.'.format(datetime.now(), batch_num))
    batch_num_val = int(math.ceil(num_val / batch_size))
    print('{}-{:d} testing batches per test.'.format(datetime.now(),
                                                     batch_num_val))

    ######################################################################
    # Placeholders
    indices = tf.placeholder(tf.int32, shape=(None, None, 2), name="indices")
    xforms = tf.placeholder(tf.float32, shape=(None, 3, 3), name="xforms")
    rotations = tf.placeholder(tf.float32,
                               shape=(None, 3, 3),
                               name="rotations")
    jitter_range = tf.placeholder(tf.float32, shape=(1), name="jitter_range")
    global_step = tf.Variable(0, trainable=False, name='global_step')
    is_training = tf.placeholder(tf.bool, name='is_training')

    is_labelled_data = tf.placeholder(tf.bool, name='is_labelled_data')

    pts_fts = tf.placeholder(tf.float32,
                             shape=(None, point_num, setting.data_dim),
                             name='pts_fts')
    labels_seg = tf.placeholder(tf.int64,
                                shape=(None, point_num),
                                name='labels_seg')
    labels_weights = tf.placeholder(tf.float32,
                                    shape=(None, point_num),
                                    name='labels_weights')

    ######################################################################
    pts_fts_sampled = tf.gather_nd(pts_fts,
                                   indices=indices,
                                   name='pts_fts_sampled')
    features_augmented = None
    if setting.data_dim > 3:
        points_sampled, features_sampled = tf.split(
            pts_fts_sampled, [3, setting.data_dim - 3],
            axis=-1,
            name='split_points_features')
        if setting.use_extra_features:
            if setting.with_normal_feature:
                if setting.data_dim < 6:
                    print('Only 3D normals are supported!')
                    exit()
                elif setting.data_dim == 6:
                    features_augmented = pf.augment(features_sampled,
                                                    rotations)
                else:
                    normals, rest = tf.split(features_sampled,
                                             [3, setting.data_dim - 6])
                    normals_augmented = pf.augment(normals, rotations)
                    features_augmented = tf.concat([normals_augmented, rest],
                                                   axis=-1)
            else:
                features_augmented = features_sampled
    else:
        points_sampled = pts_fts_sampled
    points_augmented = pf.augment(points_sampled, xforms, jitter_range)

    labels_sampled = tf.gather_nd(labels_seg,
                                  indices=indices,
                                  name='labels_sampled')
    labels_weights_sampled = tf.gather_nd(labels_weights,
                                          indices=indices,
                                          name='labels_weight_sampled')

    net = model.Net(points_augmented, features_augmented, is_training, setting)

    logits = net.logits
    probs = tf.nn.softmax(logits, name='prob')
    predictions = tf.argmax(probs, axis=-1, name='predictions')

    with tf.variable_scope('xcrf_ker_weights'):

        _, point_indices = pf.knn_indices_general(points_augmented,
                                                  points_augmented, 1024, True)

        xcrf = learningBlock(num_points=sample_num,
                             num_classes=setting.num_class,
                             theta_alpha=float(5),
                             theta_beta=float(2),
                             theta_gamma=float(1),
                             num_iterations=5,
                             name='xcrf',
                             point_indices=point_indices)

        _logits1 = xcrf.call(net.logits, points_augmented, features_augmented,
                             setting.data_dim)
        _logits2 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=2)
        _logits3 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=3)
        _logits4 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=4)
        _logits5 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=8)
        _logits6 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=16)

        _logits = _logits1 + _logits2 + _logits3 + _logits4 + _logits5 + _logits6
        _probs = tf.nn.softmax(_logits, name='probs_crf')
        _predictions = tf.argmax(_probs, axis=-1, name='predictions_crf')

    logits = tf.cond(
        is_training,
        lambda: tf.cond(is_labelled_data, lambda: _logits, lambda: logits),
        lambda: _logits)

    predictions = tf.cond(
        is_training, lambda: tf.cond(is_labelled_data, lambda: _predictions,
                                     lambda: predictions),
        lambda: _predictions)

    labels_sampled = tf.cond(
        is_training, lambda: tf.cond(is_labelled_data, lambda: labels_sampled,
                                     lambda: _predictions),
        lambda: labels_sampled)

    loss_op = tf.losses.sparse_softmax_cross_entropy(
        labels=labels_sampled, logits=logits, weights=labels_weights_sampled)

    with tf.name_scope('metrics'):
        loss_mean_op, loss_mean_update_op = tf.metrics.mean(loss_op)
        t_1_acc_op, t_1_acc_update_op = tf.metrics.accuracy(
            labels_sampled, predictions, weights=labels_weights_sampled)
        t_1_per_class_acc_op, t_1_per_class_acc_update_op = \
            tf.metrics.mean_per_class_accuracy(labels_sampled, predictions, setting.num_class,
                                               weights=labels_weights_sampled)
        t_1_per_mean_iou_op, t_1_per_mean_iou_op_update_op = \
            tf.metrics.mean_iou(labels_sampled, predictions, setting.num_class,
                                               weights=labels_weights_sampled)

    reset_metrics_op = tf.variables_initializer([
        var for var in tf.local_variables()
        if var.name.split('/')[0] == 'metrics'
    ])

    _ = tf.summary.scalar('loss/train',
                          tensor=loss_mean_op,
                          collections=['train'])
    _ = tf.summary.scalar('t_1_acc/train',
                          tensor=t_1_acc_op,
                          collections=['train'])
    _ = tf.summary.scalar('t_1_per_class_acc/train',
                          tensor=t_1_per_class_acc_op,
                          collections=['train'])

    _ = tf.summary.scalar('loss/val', tensor=loss_mean_op, collections=['val'])
    _ = tf.summary.scalar('t_1_acc/val',
                          tensor=t_1_acc_op,
                          collections=['val'])
    _ = tf.summary.scalar('t_1_per_class_acc/val',
                          tensor=t_1_per_class_acc_op,
                          collections=['val'])
    _ = tf.summary.scalar('t_1_mean_iou/val',
                          tensor=t_1_per_mean_iou_op,
                          collections=['val'])

    all_variable = tf.global_variables()

    lr_exp_op = tf.train.exponential_decay(setting.learning_rate_base,
                                           global_step,
                                           setting.decay_steps,
                                           setting.decay_rate,
                                           staircase=True)
    lr_clip_op = tf.maximum(lr_exp_op, setting.learning_rate_min)
    _ = tf.summary.scalar('learning_rate',
                          tensor=lr_clip_op,
                          collections=['train'])
    reg_loss = setting.weight_decay * tf.losses.get_regularization_loss()
    if setting.optimizer == 'adam':
        optimizer = tf.train.AdamOptimizer(learning_rate=lr_clip_op,
                                           epsilon=setting.epsilon)
    elif setting.optimizer == 'momentum':
        optimizer = tf.train.MomentumOptimizer(learning_rate=lr_clip_op,
                                               momentum=setting.momentum,
                                               use_nesterov=True)
    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

    xcrf_ker_weights = [
        var for var in tf.global_variables() if 'xcrf_ker_weights' in var.name
    ]
    no_xcrf_ker_weights = [
        var for var in tf.global_variables()
        if 'xcrf_ker_weights' not in var.name
    ]

    #print(restore_values)
    #train_op = optimizer.minimize( loss_op+reg_loss, global_step=global_step)

    with tf.control_dependencies(update_ops):
        train_op_xcrf = optimizer.minimize(loss_op + reg_loss,
                                           var_list=no_xcrf_ker_weights,
                                           global_step=global_step)
        train_op_all = optimizer.minimize(loss_op + reg_loss,
                                          var_list=all_variable,
                                          global_step=global_step)

    train_op = tf.cond(is_labelled_data, lambda: train_op_all,
                       lambda: train_op_xcrf)

    init_op = tf.group(tf.global_variables_initializer(),
                       tf.local_variables_initializer())

    saver = tf.train.Saver(var_list=no_xcrf_ker_weights, max_to_keep=5)

    saver_best = tf.train.Saver(max_to_keep=5)
    # backup all code
    code_folder = os.path.abspath(os.path.dirname(__file__))
    shutil.copytree(code_folder,
                    os.path.join(root_folder, os.path.basename(code_folder)))

    folder_ckpt = os.path.join(root_folder, 'ckpts')
    if not os.path.exists(folder_ckpt):
        os.makedirs(folder_ckpt)

    folder_ckpt_best = os.path.join(root_folder, 'ckpt-best')
    if not os.path.exists(folder_ckpt_best):
        os.makedirs(folder_ckpt_best)

    folder_summary = os.path.join(root_folder, 'summary')
    if not os.path.exists(folder_summary):
        os.makedirs(folder_summary)

    parameter_num = np.sum(
        [np.prod(v.shape.as_list()) for v in tf.trainable_variables()])
    print('{}-Parameter number: {:d}.'.format(datetime.now(),
                                              int(parameter_num)))

    _highest_val = 0.0
    max_val = 10
    with tf.Session() as sess:
        summaries_op = tf.summary.merge_all('train')
        summaries_val_op = tf.summary.merge_all('val')
        summary_values_op = tf.summary.merge_all('summary_values')
        summary_writer = tf.summary.FileWriter(folder_summary, sess.graph)

        sess.run(init_op)

        # Load the model
        if args.load_ckpt is not None:
            saver.restore(sess, args.load_ckpt)
            print('{}-Checkpoint loaded from {}!'.format(
                datetime.now(), args.load_ckpt))
        batch_num = 50000
        for batch_idx_train in range(batch_num):
            if (batch_idx_train % step_val == 0 and (batch_idx_train != 0 or args.load_ckpt is not None)) \
                    or batch_idx_train == batch_num - 1:
                ######################################################################

                # Validation
                filename_ckpt = os.path.join(folder_ckpt, 'iter')
                saver.save(sess, filename_ckpt, global_step=global_step)
                print('{}-Checkpoint saved to {}!'.format(
                    datetime.now(), filename_ckpt))

                sess.run(reset_metrics_op)
                summary_hist = None
                _idxVal = np.arange(num_val)
                np.random.shuffle(_idxVal)

                _pred = []
                _label = []
                for batch_val_idx in tqdm(range(batch_num_val)):
                    start_idx = batch_size * batch_val_idx
                    end_idx = min(start_idx + batch_size, num_val)
                    batch_size_val = end_idx - start_idx

                    points_batch = data_val[_idxVal[start_idx:end_idx], ...]
                    points_num_batch = data_num_val[_idxVal[start_idx:end_idx],
                                                    ...]
                    labels_batch = label_val[_idxVal[start_idx:end_idx], ...]

                    weights_batch = np.array(label_weights_val)[labels_batch]

                    xforms_np, rotations_np = pf.get_xforms(
                        batch_size_val,
                        rotation_range=rotation_range_val,
                        scaling_range=scaling_range_val,
                        order=setting.rotation_order)

                    _labels_sampled, _predictions, _, _, _, _ = sess.run(
                        [
                            labels_sampled, predictions, loss_mean_update_op,
                            t_1_acc_update_op, t_1_per_class_acc_update_op,
                            t_1_per_mean_iou_op_update_op
                        ],
                        feed_dict={
                            pts_fts:
                            points_batch,
                            indices:
                            pf.get_indices(batch_size_val, sample_num,
                                           points_num_batch),
                            xforms:
                            xforms_np,
                            rotations:
                            rotations_np,
                            jitter_range:
                            np.array([jitter_val]),
                            labels_seg:
                            labels_batch,
                            is_labelled_data:
                            True,
                            labels_weights:
                            weights_batch,
                            is_training:
                            False,
                        })
                    _pred.append(_predictions.flatten())
                    _label.append(_labels_sampled.flatten())

                loss_val, t_1_acc_val, t_1_per_class_acc_val, t1__mean_iou, summaries_val = sess.run(
                    [
                        loss_mean_op, t_1_acc_op, t_1_per_class_acc_op,
                        t_1_per_mean_iou_op, summaries_val_op
                    ])

                img_d_summary = pf.plot_confusion_matrix(
                    _label,
                    _pred, ["Environment", "Pedestrian", "Car", "Cyclist"],
                    tensor_name='confusion_matrix')

                max_val = max_val - 1

                if (t_1_per_class_acc_val > _highest_val):

                    max_val = 10

                    _highest_val = t_1_per_class_acc_val

                    filename_ckpt = os.path.join(folder_ckpt_best,
                                                 str(_highest_val) + "-iter-")
                    saver_best.save(sess,
                                    filename_ckpt,
                                    global_step=global_step)

                if (max_val < 0):
                    sys.exit(0)

                summary_writer.add_summary(summaries_val, batch_idx_train)

                summary_writer.add_summary(img_d_summary, batch_idx_train)

                #summary_writer.add_summary(summary_hist, batch_idx_train)
                print(
                    '{}-[Val  ]-Average:      Loss: {:.4f}  T-1 Acc: {:.4f}  Diff-Best: {:.4f} T-1 mAcc: {:.4f}   T-1 mIoU: {:.4f}'
                    .format(datetime.now(), loss_val, t_1_acc_val,
                            _highest_val - t_1_per_class_acc_val,
                            t_1_per_class_acc_val, t1__mean_iou))
                sys.stdout.flush()
                ######################################################################

                # Unseen Data
                sess.run(reset_metrics_op)
                summary_hist = None
                _idxunseen = np.arange(num_unseen)
                np.random.shuffle(_idxunseen)

                _pred = []
                _label = []

                unseenIndices = np.arange(batch_num_unseen)
                np.random.shuffle(unseenIndices)
                for batch_val_idx in tqdm(unseenIndices[:500]):
                    start_idx = batch_size * batch_val_idx
                    end_idx = min(start_idx + batch_size, num_unseen)
                    batch_size_train = end_idx - start_idx

                    points_batch = data_unseen[_idxunseen[start_idx:end_idx],
                                               ...]
                    points_num_batch = data_num_unseen[
                        _idxunseen[start_idx:end_idx], ...]
                    labels_batch = np.zeros(points_batch.shape[0:2],
                                            dtype=np.int32)

                    weights_batch = np.array(label_weights_list)[labels_batch]

                    xforms_np, rotations_np = pf.get_xforms(
                        batch_size_train,
                        rotation_range=rotation_range,
                        scaling_range=scaling_range,
                        order=setting.rotation_order)

                    offset = int(
                        random.gauss(0,
                                     sample_num * setting.sample_num_variance))
                    offset = max(offset, -sample_num * setting.sample_num_clip)
                    offset = min(offset, sample_num * setting.sample_num_clip)
                    sample_num_train = sample_num + offset

                    sess.run(
                        [
                            train_op, loss_mean_update_op, t_1_acc_update_op,
                            t_1_per_class_acc_update_op,
                            t_1_per_mean_iou_op_update_op
                        ],
                        feed_dict={
                            pts_fts:
                            points_batch,
                            is_labelled_data:
                            False,
                            indices:
                            pf.get_indices(batch_size_train, sample_num_train,
                                           points_num_batch),
                            xforms:
                            xforms_np,
                            rotations:
                            rotations_np,
                            jitter_range:
                            np.array([jitter]),
                            labels_seg:
                            labels_batch,
                            labels_weights:
                            weights_batch,
                            is_training:
                            True,
                        })
                    if batch_val_idx % 100 == 0:
                        loss, t_1_acc, t_1_per_class_acc, t_1__mean_iou = sess.run(
                            [
                                loss_mean_op, t_1_acc_op, t_1_per_class_acc_op,
                                t_1_per_mean_iou_op
                            ],
                            feed_dict={
                                pts_fts:
                                points_batch,
                                indices:
                                pf.get_indices(batch_size_train,
                                               sample_num_train,
                                               points_num_batch),
                                xforms:
                                xforms_np,
                                is_labelled_data:
                                False,
                                rotations:
                                rotations_np,
                                jitter_range:
                                np.array([jitter]),
                                labels_seg:
                                labels_batch,
                                labels_weights:
                                weights_batch,
                                is_training:
                                True,
                            })
                        print(
                            '{}-[Train]-Unseen: {:06d}  Loss: {:.4f}  T-1 Acc: {:.4f}  T-1 mAcc: {:.4f}   T-1 mIoU: {:.4f}'
                            .format(datetime.now(), batch_val_idx, loss,
                                    t_1_acc, t_1_per_class_acc, t_1__mean_iou))
                        sys.stdout.flush()

            ######################################################################
            # Training
            start_idx = (batch_size * batch_idx_train) % num_train
            end_idx = min(start_idx + batch_size, num_train)
            batch_size_train = end_idx - start_idx
            points_batch = data_train[start_idx:end_idx, ...]
            points_num_batch = data_num_train[start_idx:end_idx, ...]
            labels_batch = label_train[start_idx:end_idx, ...]
            weights_batch = np.array(label_weights_list)[labels_batch]

            if start_idx + batch_size_train == num_train:
                if is_list_of_h5_list:
                    filelist_train_prev = seg_list[(seg_list_idx - 1) %
                                                   len(seg_list)]
                    filelist_train = seg_list[seg_list_idx % len(seg_list)]
                    if filelist_train != filelist_train_prev:
                        data_train, _, data_num_train, label_train, _ = data_utils.load_seg(
                            filelist_train)
                        num_train = data_train.shape[0]
                    seg_list_idx = seg_list_idx + 1
                data_train, data_num_train, label_train = \
                    data_utils.grouped_shuffle([data_train, data_num_train, label_train])

            offset = int(
                random.gauss(0, sample_num * setting.sample_num_variance))
            offset = max(offset, -sample_num * setting.sample_num_clip)
            offset = min(offset, sample_num * setting.sample_num_clip)
            sample_num_train = sample_num + offset
            xforms_np, rotations_np = pf.get_xforms(
                batch_size_train,
                rotation_range=rotation_range,
                scaling_range=scaling_range,
                order=setting.rotation_order)
            sess.run(reset_metrics_op)
            sess.run(
                [
                    train_op, loss_mean_update_op, t_1_acc_update_op,
                    t_1_per_class_acc_update_op, t_1_per_mean_iou_op_update_op
                ],
                feed_dict={
                    pts_fts:
                    points_batch,
                    is_labelled_data:
                    True,
                    indices:
                    pf.get_indices(batch_size_train, sample_num_train,
                                   points_num_batch),
                    xforms:
                    xforms_np,
                    rotations:
                    rotations_np,
                    jitter_range:
                    np.array([jitter]),
                    labels_seg:
                    labels_batch,
                    labels_weights:
                    weights_batch,
                    is_training:
                    True,
                })
            if batch_idx_train % 100 == 0:
                loss, t_1_acc, t_1_per_class_acc, t_1__mean_iou, summaries = sess.run(
                    [
                        loss_mean_op, t_1_acc_op, t_1_per_class_acc_op,
                        t_1_per_mean_iou_op, summaries_op
                    ],
                    feed_dict={
                        pts_fts:
                        points_batch,
                        indices:
                        pf.get_indices(batch_size_train, sample_num_train,
                                       points_num_batch),
                        xforms:
                        xforms_np,
                        is_labelled_data:
                        True,
                        rotations:
                        rotations_np,
                        jitter_range:
                        np.array([jitter]),
                        labels_seg:
                        labels_batch,
                        labels_weights:
                        weights_batch,
                        is_training:
                        True,
                    })
                summary_writer.add_summary(summaries, batch_idx_train)
                print(
                    '{}-[Train]-Iter: {:06d}  Loss: {:.4f}  T-1 Acc: {:.4f}  T-1 mAcc: {:.4f}   T-1 mIoU: {:.4f}'
                    .format(datetime.now(), batch_idx_train, loss, t_1_acc,
                            t_1_per_class_acc, t_1__mean_iou))
                sys.stdout.flush()
            ######################################################################
        print('{}-Done!'.format(datetime.now()))
Exemple #15
0
def ficonv(pts,
           fts,
           qrs,
           tag,
           N,
           K1,
           mm,
           sigma,
           scale,
           K,
           D,
           P,
           C,
           C_pts_fts,
           kernel_num,
           is_training,
           with_kernel_registering,
           with_kernel_shape_comparison,
           with_point_transformation,
           with_feature_transformation,
           with_learning_feature_transformation,
           kenel_initialization_method,
           depth_multiplier,
           sorting_method=None,
           with_global=False):
    Dis, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
    indices = indices_dilated[:, :, ::D, :]

    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)

    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  # (N, P, K, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2,
                                   name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(nn_pts,
                               nn_pts_center,
                               name=tag + 'nn_pts_local')  # (N, P, K, 3)
    if with_point_transformation or with_feature_transformation:
        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training, (1, K))
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training, (1, K))
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        X_2 = pf.depthwise_conv2d(X_1_KK,
                                  K,
                                  tag + 'X_2',
                                  is_training, (1, K),
                                  activation=None)
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
    if with_point_transformation:
        if with_learning_feature_transformation:
            nn_pts_local = tf.matmul(X_2_KK, nn_pts_local)
            # Prepare features to be transformed
            nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                         tag + 'nn_fts_from_pts_0',
                                         is_training)
            nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                                       tag + 'nn_fts_from_pts', is_training)
        else:
            # Prepare features to be transformed
            nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                         tag + 'nn_fts_from_pts_0',
                                         is_training)
            nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                                       tag + 'nn_fts_from_pts', is_training)
            nn_pts_local = tf.matmul(X_2_KK, nn_pts_local)
    else:
        if with_learning_feature_transformation:
            nn_pts_local_ = tf.matmul(X_2_KK,
                                      nn_pts_local,
                                      name=tag + 'nn_pts_local_')
            # Prepare features to be transformed
            nn_fts_from_pts_0 = pf.dense(nn_pts_local_, C_pts_fts,
                                         tag + 'nn_fts_from_pts_0',
                                         is_training)
            nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                                       tag + 'nn_fts_from_pts', is_training)
        else:
            nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts,
                                         tag + 'nn_fts_from_pts_0',
                                         is_training)
            nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts,
                                       tag + 'nn_fts_from_pts', is_training)

    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts,
                                        indices,
                                        name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev],
                                 axis=-1,
                                 name=tag + 'nn_fts_input')

    P1 = tf.shape(nn_pts_local)[1]
    dim1 = 3
    if with_kernel_registering:
        ######################## preparing #########################
        if with_feature_transformation:
            nn_fts_input = tf.matmul(X_2_KK, nn_fts_input)

        r_data = tf.reduce_sum(nn_pts_local * nn_pts_local,
                               axis=3,
                               keep_dims=True,
                               name=tag + 'kernel_pow')
        ######################## kernel-registering #########################
        shape_id = 0
        if kenel_initialization_method == 'random':
            kernel_shape = tf.Variable(tf.random_uniform([K1, dim1],
                                                         minval=-0.5,
                                                         maxval=0.5,
                                                         dtype=tf.float32),
                                       name=tag + 'kernel_shape' +
                                       str(shape_id))
        else:
            kernel_shape = tf.Variable(tf.random_normal([K1, dim1],
                                                        mean=0.0,
                                                        stddev=1.0,
                                                        dtype=tf.float32),
                                       name=tag + 'kernel_shape' +
                                       str(shape_id))
        kernel_shape_dis = tf.sqrt(tf.reduce_sum(kernel_shape * kernel_shape,
                                                 axis=1),
                                   name=tag + 'kernel_shape_dis' +
                                   str(shape_id))
        kernel_shape_normal = scale * tf.div(
            kernel_shape,
            tf.reduce_max(kernel_shape_dis),
            name=tag + 'kernel_shape_normal' + str(shape_id))

        r_kernel = tf.reduce_sum(kernel_shape_normal * kernel_shape_normal,
                                 axis=1,
                                 keep_dims=True,
                                 name=tag + 'kernel_pow' + str(shape_id))
        reshape_data = tf.reshape(nn_pts_local, [N * P1 * K, dim1],
                                  name=tag + 'reshape_kernel' + str(shape_id))
        m = tf.reshape(tf.matmul(reshape_data,
                                 tf.transpose(kernel_shape_normal)),
                       [N, P1, K, K1],
                       name=tag + 'mm' + str(shape_id))
        dis_matrix = tf.transpose(r_data - 2 * m + tf.transpose(r_kernel),
                                  perm=[0, 1, 3, 2],
                                  name=tag + 'dis_matrix' + str(shape_id))
        coef_matrix = tf.exp(tf.div(-dis_matrix, sigma),
                             name=tag + 'coef_matrix' + str(shape_id))
        #coef_matrix = tf.transpose(r_data-2*m+tf.transpose(r_kernel),perm=[0,1,3,2],name=tag+'coef_matrix'+str(shape_id))
        if with_kernel_shape_comparison:
            coef_global = tf.reduce_sum(
                coef_matrix, axis=[2, 3], keep_dims=True) / K
            coef_normal = coef_global * tf.div(
                coef_matrix,
                tf.reduce_sum(coef_matrix, axis=3, keep_dims=True),
                name=tag + 'coef_normal' + str(shape_id))
        else:
            coef_normal = tf.div(coef_matrix,
                                 tf.reduce_sum(coef_matrix,
                                               axis=3,
                                               keep_dims=True),
                                 name=tag + 'coef_normal' + str(shape_id))

        fts_X = tf.matmul(coef_normal,
                          nn_fts_input,
                          name=tag + 'fts_X' + str(shape_id))
        ###################################################################
        fts_conv = pf.separable_conv2d(fts_X,
                                       math.ceil(mm * C / kernel_num),
                                       tag + 'fts_conv' + str(shape_id),
                                       is_training, (1, K1),
                                       depth_multiplier=depth_multiplier)
        fts_conv_3d = tf.squeeze(fts_conv,
                                 axis=2,
                                 name=tag + 'fts_conv_3d' + str(shape_id))

        for shape_id in range(kernel_num - 1):
            shape_id = shape_id + 1
            if kenel_initialization_method == 'random':
                kernel_shape = tf.Variable(tf.random_uniform([K1, dim1],
                                                             minval=-0.5,
                                                             maxval=0.5,
                                                             dtype=tf.float32),
                                           name=tag + 'kernel_shape' +
                                           str(shape_id))
            else:
                kernel_shape = tf.Variable(tf.random_normal([K1, dim1],
                                                            mean=0.0,
                                                            stddev=1.0,
                                                            dtype=tf.float32),
                                           name=tag + 'kernel_shape' +
                                           str(shape_id))
            kernel_shape_dis = tf.sqrt(
                tf.reduce_sum(kernel_shape * kernel_shape, axis=1),
                name=tag + 'kernel_shape_dis' + str(shape_id))
            kernel_shape_normal = scale * tf.div(
                kernel_shape,
                tf.reduce_max(kernel_shape_dis),
                name=tag + 'kernel_shape_normal' + str(shape_id))

            r_kernel = tf.reduce_sum(kernel_shape_normal * kernel_shape_normal,
                                     axis=1,
                                     keep_dims=True,
                                     name=tag + 'kernel_pow' + str(shape_id))
            reshape_data = tf.reshape(nn_pts_local, [N * P1 * K, dim1],
                                      name=tag + 'reshape_kernel' +
                                      str(shape_id))
            m = tf.reshape(tf.matmul(reshape_data,
                                     tf.transpose(kernel_shape_normal)),
                           [N, P1, K, K1],
                           name=tag + 'mm' + str(shape_id))
            dis_matrix = tf.transpose(r_data - 2 * m + tf.transpose(r_kernel),
                                      perm=[0, 1, 3, 2],
                                      name=tag + 'dis_matrix' + str(shape_id))
            coef_matrix = tf.exp(tf.div(-dis_matrix, sigma),
                                 name=tag + 'coef_matrix' + str(shape_id))
            #coef_matrix = tf.transpose(r_data-2*m+tf.transpose(r_kernel),perm=[0,1,3,2],name=tag+'coef_matrix'+str(shape_id))
            if with_kernel_shape_comparison:
                coef_global = tf.reduce_sum(
                    coef_matrix, axis=[2, 3], keep_dims=True) / K
                coef_normal = coef_global * tf.div(
                    coef_matrix,
                    tf.reduce_sum(coef_matrix, axis=3, keep_dims=True),
                    name=tag + 'coef_normal' + str(shape_id))
            else:
                coef_normal = tf.div(coef_matrix,
                                     tf.reduce_sum(coef_matrix,
                                                   axis=3,
                                                   keep_dims=True),
                                     name=tag + 'coef_normal' + str(shape_id))

            fts_X = tf.matmul(coef_normal,
                              nn_fts_input,
                              name=tag + 'fts_X' + str(shape_id))
            ###################################################################
            fts_conv = pf.separable_conv2d(fts_X,
                                           math.ceil(mm * C / kernel_num),
                                           tag + 'fts_conv' + str(shape_id),
                                           is_training, (1, K1),
                                           depth_multiplier=depth_multiplier)
            fts_conv_3d = tf.concat(
                [fts_conv_3d, tf.squeeze(fts_conv, axis=2)],
                axis=-1,
                name=tag + 'fts_conv_3d' + str(shape_id))
    else:
        fts_X = nn_fts_input
        fts_conv = pf.separable_conv2d(fts_X,
                                       C,
                                       tag + 'fts_conv',
                                       is_training, (1, K),
                                       depth_multiplier=depth_multiplier)
        fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')

    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global',
                              is_training)
        return tf.concat([fts_global, fts_conv_3d],
                         axis=-1,
                         name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
Exemple #16
0
def point_conv_transpose_1(pts_in,
                           fts_in,
                           pts_out,
                           indices,
                           K,
                           D,
                           C,
                           is_training,
                           activation=tf.nn.relu,
                           bn=True,
                           sorting_method='cxyz',
                           tag='point_conv'):
    '''
    pts_in: xyz points, Tensor, (batch_size, npoint, 3)
    fts_in: features, Tensor, (batch_size, npoint, channel_fts)
    pts_out: xyz points, Tensor, (batch_size, n_pts_out, 3), NOTE: pts_out is denser than pts.
    K: number of neighbors for one query, scalar, int
    D: dilation rate
    C: output feature channel number

    in the comments:
        N - batch size
        P - number of pts_in
        K - number of neighbors
    '''
    batch_size = pts_out.shape[0]
    n_pts_out = pts_out.shape[1]
    n_pts_in = pts_in.shape[1]

    if indices == None:
        _, indices_dilated = pf.knn_indices_general(pts_in, pts_out, K * D,
                                                    True)
        indices = indices_dilated[:, :, ::D, :]

        if sorting_method is not None:
            indices = pf.sort_points(pts_out, indices,
                                     sorting_method)  # (N, P, K, 2)

    # move the gathered pts_out to local frame
    nn_pts = tf.gather_nd(pts_out, indices,
                          name=tag + '_nn_pts')  # (N, P, K, 3)
    nn_pts_center = tf.expand_dims(pts_in, axis=2,
                                   name=tag + '_nn_pts_center')  # (N, P, 1, 3)
    nn_pts_local = tf.subtract(nn_pts,
                               nn_pts_center,
                               name=tag + '_nn_pts_local')  # (N, P, K, 3)

    if fts_in is None:
        #nn_fts_input = pts_in # (N, P, 3)
        print('Error: fts_in is None.')
    else:
        #nn_fts_input = tf.concat([pts_in, fts_in], axis=-1, name=tag + 'nn_fts_input') # (N, P, 3+C_fts_in)
        nn_fts_input = fts_in  # (N, P, C_fts_in)

    nn_fts_input = tf.expand_dims(nn_fts_input, axis=2)  # (N, P, 1, C_fts_in)

    # TODO: maybe try other conv1d? fc? there
    fts_conv = pf.conv2d_transpose(
        nn_fts_input,
        C,
        tag + '_fts_deconv',
        is_training,
        kernel_size=(1, 1),
        strides=(1, K),
        with_bn=bn,
        activation=activation)  # (N, P, 1, C_fts_in) -> (N, P, K, C)

    fts_conv_pts_out_concat = tf.concat([fts_conv, nn_pts_local],
                                        axis=-1)  # (N, P, K, C+3)

    fts_pts_conv = pf.conv2d(
        fts_conv_pts_out_concat,
        C,
        tag + '_fts_pts_conv1D',
        is_training,
        kernel_size=(1, 1),
        strides=(1, 1),
        with_bn=bn,
        activation=activation)  # (N, P, K, C+3) -> (N, P, K, C)

    ######## collect fts for points in the output #######################
    ##########################################################################
    npoint_indices = tf.reshape(tf.range(n_pts_in), [-1, 1])  # (P, 1)
    npoint_indices = tf.tile(npoint_indices, (1, K))  # (P, K)
    npoint_indices = tf.expand_dims(npoint_indices, axis=0)  # (1, P, K)
    npoint_indices = tf.tile(npoint_indices, (batch_size, 1, 1))  # (N, P, K)
    npoint_indices = tf.expand_dims(npoint_indices, axis=3)  # (N, P, K, 1)
    NPK_indices = tf.concat(
        [
            tf.expand_dims(indices[:, :, :, 0], axis=-1), npoint_indices,
            tf.expand_dims(indices[:, :, :, 1], axis=-1)
        ],
        axis=3
    )  # (N, P, K, 3) last dim is 3 for (batch_idx, point_idx, neighbor_idx)

    NPK_indices = tf.expand_dims(NPK_indices, axis=3)  # (N, P, K, 1, 3)
    NPK_indices = tf.tile(NPK_indices, (1, 1, 1, C, 1))  # (N, P, K, C, 3)

    channel_indices = tf.reshape(tf.range(C),
                                 [1, 1, 1, -1, 1])  # (1, 1, 1, C, 1)
    channel_indices = tf.tile(
        channel_indices, (batch_size, n_pts_in, K, 1, 1))  # (N, P, K, C, 1)

    final_indices = tf.concat(
        [NPK_indices, channel_indices], axis=4
    )  # (N, P, K, C, 4) last dim: (batch_idx, rep_point_idx, neighbor_idx_in_pts_out, channel_idx)

    ######## reshape for constructing sparse tensor ################
    final_indices = tf.reshape(final_indices, [-1, 4])
    final_value = tf.reshape(fts_pts_conv, [-1])

    # using sparse tensor and sparse ops
    # TODO: in tf 1.4, the sparse_reduce_sum cannot infer the shape even if the input shape is known.
    #       so, try to use latest tf, a version of after Oct. 2018.
    #fts_sparse = tf.SparseTensor(indices=tf.cast(final_indices, tf.int64), values=final_value, dense_shape=[batch_size, n_pts_in, n_pts_out, C]) # (N, P, N_PTS_OUT, C) in sparse representation
    #fts_out = tf.sparse_reduce_sum(fts_sparse, axis=1) # (N, N_PTS_OUT, C)
    #fts_dense = tf.sparse_tensor_to_dense(fts_sparse, default_value=0, validate_indices=False) # (N, P, N_PTS_OUT, C)

    # using dense tensor, memory-consuming
    fts_dense = tf.scatter_nd(
        final_indices, final_value,
        [batch_size, n_pts_in, n_pts_out, C])  # (N, P, N_PTS_OUT, C)
    fts_out = tf.reduce_sum(fts_dense, axis=1)  # (N, N_PTS_OUT, C)

    return fts_out
Exemple #17
0
def main(FLAG):

    data_filename = FLAG.filename
    retrieve_whole_files = FLAG.retrieve_whole_files

    CHECKPOINT_LOCATION = "/home/hasan/data/hdd8TB/KITTI/3D-object-detection/pointcnn-model/AXCRF-random-XYZ-grid-0.25/pointcnn_seg_kitti3d_x8_2048_fps_2019-04-11-14-43-24_31566/ckpt-best/0.9320669-iter--237975"
    max_point_num = 8192

    # Root directory of the project

    # data_filename = FLAGS.image_filename
    ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
    print(ROOT_DIR)

    # Directory to save logs and trained model
    MODEL_DIR = os.path.join(ROOT_DIR, "logs")

    # Directory to get binary and image data
    PARENT_DIR = os.path.abspath(os.path.join(ROOT_DIR, os.pardir))
    DATA_DIR = os.path.join(PARENT_DIR, "test_dataset")

    ## Feed data to pointCNN

    args_load_ckpt = CHECKPOINT_LOCATION
    args_model = "pointcnn_seg"
    args_setting = "kitti3d_x8_2048_fps"
    args_repeat_num = 1

    model = importlib.import_module(args_model)
    setting_path = os.path.join(os.path.dirname(__file__), args_model)
    sys.path.append(setting_path)
    setting = importlib.import_module(args_setting)

    sample_num = setting.sample_num
    batch_size = args_repeat_num * int(math.ceil(max_point_num / sample_num))

    ######################################################################
    # Placeholders
    indices = tf.placeholder(tf.int32,
                             shape=(batch_size, None, 2),
                             name="indices")
    is_training = tf.placeholder(tf.bool, name='is_training')
    pts_fts = tf.placeholder(tf.float32,
                             shape=(batch_size, max_point_num,
                                    setting.data_dim),
                             name='points')
    ######################################################################

    ######################################################################
    pts_fts_sampled = tf.gather_nd(pts_fts,
                                   indices=indices,
                                   name='pts_fts_sampled')
    if setting.data_dim > 3:
        points_sampled, features_sampled = tf.split(
            pts_fts_sampled, [3, setting.data_dim - 3],
            axis=-1,
            name='split_points_features')
        if not setting.use_extra_features:
            features_sampled = None
    else:
        points_sampled = pts_fts_sampled
        features_sampled = None

    net = model.Net(points_sampled, features_sampled, is_training, setting)

    points_augmented = points_sampled
    features_augmented = features_sampled

    with tf.variable_scope('xcrf_ker_weights'):

        _, point_indices = pf.knn_indices_general(points_augmented,
                                                  points_augmented, 1024, True)

        xcrf = learningBlock(num_points=sample_num,
                             num_classes=setting.num_class,
                             theta_alpha=float(5),
                             theta_beta=float(2),
                             theta_gamma=float(1),
                             num_iterations=10,
                             name='xcrf',
                             point_indices=point_indices)

        _logits1 = xcrf.call(net.logits, points_augmented, features_augmented,
                             setting.data_dim)
        _logits2 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=2)
        _logits3 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=3)
        _logits4 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=4)
        _logits5 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=8)
        _logits6 = xcrf.call(net.logits,
                             points_augmented,
                             features_augmented,
                             setting.data_dim,
                             D=16)

        _logits = _logits1 + _logits2 + _logits3 + _logits4 + _logits5 + _logits6

    seg_probs_op = tf.nn.softmax(_logits, name='seg_probs')

    # for restore model
    saver = tf.train.Saver()

    parameter_num = np.sum(
        [np.prod(v.shape.as_list()) for v in tf.trainable_variables()])
    print('{}-Parameter number: {:d}.'.format(datetime.now(), parameter_num))

    with tf.Session() as sess:
        # Load the model
        saver.restore(sess, args_load_ckpt)
        print('{}-Checkpoint loaded from {}!'.format(datetime.now(),
                                                     args_load_ckpt))

        indices_batch_indices = np.tile(
            np.reshape(np.arange(batch_size), (batch_size, 1, 1)),
            (1, sample_num, 1))

        drivename, fname = data_filename.split("/")

        all_files = [fname]
        if (retrieve_whole_files):
            source_path = os.path.join(DATA_DIR, drivename, "bin_data")

            glob_files = (glob.glob(source_path + "/*.bin"))

            all_files = [fname] + [
                _i.replace(source_path + "/", "")[0:-4] for _i in glob_files
            ]

        for fname in all_files:

            output_location = os.path.join(
                ROOT_DIR, "output/" + drivename + "_" + fname + "axcrf.bin")

            if os.path.isfile(output_location):
                continue

            label_length, data, data_num, indices_split_to_full, item_num, all_label_pred, indices_for_prediction = data_preprocessing(
                drivename, fname, max_point_num)

            merged_label_zero = np.zeros((label_length), dtype=int)
            merged_confidence_zero = np.zeros((label_length), dtype=float)

            data = data[0:item_num, ...].astype(np.float32)

            data_num = data_num[0:item_num, ...]
            indices_split_to_full = indices_split_to_full[0:item_num]

            batch_num = data.shape[0]

            labels_pred = np.full((batch_num, max_point_num),
                                  -1,
                                  dtype=np.int32)
            confidences_pred = np.zeros((batch_num, max_point_num),
                                        dtype=np.float32)

            for batch_idx in range(batch_num):
                points_batch = data[[batch_idx] * batch_size, ...]
                point_num = data_num[batch_idx]

                tile_num = int(math.ceil(
                    (sample_num * batch_size) / point_num))
                indices_shuffle = np.tile(np.arange(point_num),
                                          tile_num)[0:sample_num * batch_size]
                np.random.shuffle(indices_shuffle)
                indices_batch_shuffle = np.reshape(indices_shuffle,
                                                   (batch_size, sample_num, 1))
                indices_batch = np.concatenate(
                    (indices_batch_indices, indices_batch_shuffle), axis=2)

                seg_probs = sess.run(
                    [seg_probs_op],
                    feed_dict={
                        pts_fts: points_batch,
                        indices: indices_batch.astype(np.int32),
                        is_training: False,
                    })
                probs_2d = np.reshape(seg_probs, (sample_num * batch_size, -1))

                predictions = [(-1, 0.0)] * point_num
                for idx in range(sample_num * batch_size):
                    point_idx = indices_shuffle[idx]
                    probs = probs_2d[idx, :]
                    confidence = np.amax(probs)
                    label = np.argmax(probs)
                    if confidence > predictions[point_idx][1]:
                        predictions[point_idx] = [label, confidence]
                labels_pred[batch_idx, 0:point_num] = np.array(
                    [label for label, _ in predictions])
                confidences_pred[batch_idx, 0:point_num] = np.array(
                    [confidence for _, confidence in predictions])

            for idx in range(batch_num):  #Get highest confidence
                pred = labels_pred[idx].astype(np.int64)
                _indices = indices_split_to_full[idx].astype(np.int64)
                confidence = confidences_pred[idx].astype(np.float32)
                num = data_num[idx].astype(np.int64)

                for i in range(pred.shape[0]):

                    if confidence[i] > 0.8 and confidence[
                            i] > merged_confidence_zero[_indices[i]]:
                        merged_confidence_zero[_indices[i]] = confidence[i]
                        merged_label_zero[_indices[i]] = pred[i]

            print('{}-Done!'.format(datetime.now()))
            all_label_pred[indices_for_prediction] = merged_label_zero

            bounded_indices = (all_label_pred == 2).nonzero()[0]

            bounded_indices.tofile(output_location)

    return

    #Remove Ground points
    """
Exemple #18
0
    def __init__(self, points, features, num_class, is_training, setting, task):
        xconv_params = setting.xconv_params
        fc_params = setting.fc_params
        with_X_transformation = setting.with_X_transformation
        sorting_method = setting.sorting_method
        N = tf.shape(points)[0]

        fts_T,fts_T1 = pointnet1(points, is_training, N, PP=1024)

        if setting.with_fps:
            from sampling import tf_sampling

        self.layer_pts = [points]

        qrs_indices = pf.knn_indices_general(points, points, 1, False)
        self.layer_qrs_indices = [qrs_indices]

        if features is None:
            self.layer_fts = [features]
        else:
            C_fts = xconv_params[0][-1] // 2
            features_hd = pf.dense(features, C_fts, 'features_hd', is_training)
            self.layer_fts = [features_hd]

        for layer_idx, layer_param in enumerate(xconv_params):
            tag = 'xconv_' + str(layer_idx + 1) + '_'
            K, D, P, C = layer_param

            # get k-nearest points
            pts = self.layer_pts[-1]
            fts = self.layer_fts[-1]
            if P == -1:
                qrs = points
                _, qrs_indices = pf.knn_indices_general(qrs, points, 1, False)   #### zj 2018/04/10
            else:
                if setting.with_fps:
                    qrs = tf_sampling.gather_point(pts, qrs_indices)  # (N,P,3)
                else:
                    qrs = tf.slice(pts, (0, 0, 0), (-1, P, -1), name=tag + 'qrs')  # (N, P, 3)
                _, qrs_indices = pf.knn_indices_general(qrs, points, 1, False)     #### zj 2018/04/10

            print(qrs_indices)                                    #### zj 2018/04/10
            self.layer_qrs_indices.append(qrs_indices)
            self.layer_pts.append(qrs)
 
            if layer_idx == 0:
                C_pts_fts = C // 2 if fts is None else C // 4
                depth_multiplier = 4
            else:
                C_prev = xconv_params[layer_idx - 1][-1]
                C_pts_fts = C_prev // 4
                depth_multiplier = math.ceil(C / C_prev)
            fts_xconv = xconv(pts, fts, qrs, tag, N, K, D, P, C, C_pts_fts, is_training, with_X_transformation,
                              depth_multiplier, sorting_method)
            self.layer_fts.append(fts_xconv)

        if task == 'segmentation':
            for layer_idx, layer_param in enumerate(setting.xdconv_params):
                tag = 'xdconv_' + str(layer_idx + 1) + '_'
                K, D, pts_layer_idx, qrs_layer_idx = layer_param

                pts = self.layer_pts[pts_layer_idx + 1]
                fts = self.layer_fts[pts_layer_idx + 1] if layer_idx == 0 else self.layer_fts[-1]
                qrs = self.layer_pts[qrs_layer_idx + 1]
                fts_qrs = self.layer_fts[qrs_layer_idx + 1]
                _, _, P, C = xconv_params[qrs_layer_idx]
                _, _, _, C_prev = xconv_params[pts_layer_idx]
                C_pts_fts = C_prev // 4
                depth_multiplier = 1
                fts_xdconv = xconv(pts, fts, qrs, tag, N, K, D, P, C, C_pts_fts, is_training, with_X_transformation,
                                   depth_multiplier, sorting_method)
                fts_concat = tf.concat([fts_xdconv, fts_qrs], axis=-1, name=tag + 'fts_concat')
                fts_fuse = pf.dense(fts_concat, C, tag + 'fts_fuse', is_training)
                self.layer_pts.append(qrs)
                self.layer_fts.append(fts_fuse)
        #################################### zj,2018/04/10####################################
        self.fc_layers1 = self.layer_fts[-1]
        self.fc_layers_indices = self.layer_qrs_indices[-1]
        #last_ftsT = tf.gather_nd(fts_T, self.fc_layers_indices , name='last_ftsT')
        #last_ftsT = tf.squeeze(last_ftsT, axis=2, name='last_ftsT1')
        nn = tf.shape(self.fc_layers1)[1]
        last_ftsT = tf.tile(fts_T, (1,nn,1),name = 'last_ftsT')
        self.fc_layers1 = tf.concat([self.fc_layers1, last_ftsT] ,axis = -1, name='last_ftsT_concat')
        self.fc_layers = [self.fc_layers1]
        print(self.fc_layers)
        #######################################################################################

        for layer_idx, layer_param in enumerate(fc_params):
            channel_num, drop_rate = layer_param
            fc = pf.dense(self.fc_layers[-1], channel_num, 'fc{:d}'.format(layer_idx), is_training)  #[N,128,channel_num]???
            fc_drop = tf.layers.dropout(fc, drop_rate, training=is_training, name='fc{:d}_drop'.format(layer_idx))
            self.fc_layers.append(fc_drop)

        logits = pf.dense(self.fc_layers[-1], num_class, 'logits', is_training, with_bn=False, activation=None) #[N,128,40?]???
        if task == 'classification':
            logits_mean = tf.reduce_mean(logits, axis=1, keep_dims=True, name='logits_mean')
            self.logits = tf.cond(is_training, lambda: logits, lambda: logits_mean)
        elif task == 'segmentation':
            self.logits = logits
        else:
            print('Unknown task!')
            exit()
        self.probs = tf.nn.softmax(self.logits, name='probs')
def xconv(pts, fts, qrs, tag, N, K, D, P, C, C_pts_fts, is_training, with_X_transformation, depth_multiplier,
          sorting_method=None, with_global=False):
'''
pts: batch points input
fts: batch feature inputs
qrs: points outputs
tag: tags
N: batch_size
K: k neighbors, like kernel size in normal conv2d
D: dilation rate
P: sample points size in one sample  
C: input channel num
C_pts_fts: output channel num
'''
    # get k*d near points'indices from pts according to qrs
    _, indices_dilated = pf.knn_indices_general(qrs, pts, K * D, True)
    # select k points' indices
    indices = indices_dilated[:, :, ::D, :]

    if sorting_method is not None:
        indices = pf.sort_points(pts, indices, sorting_method)
    # get the k points' data according to the indices
    nn_pts = tf.gather_nd(pts, indices, name=tag + 'nn_pts')  # (N, P, K, 3)
    # expand qrs from (N, P, 3) to (N, P, 1, 3)
    nn_pts_center = tf.expand_dims(qrs, axis=2, name=tag + 'nn_pts_center')  # (N, P, 1, 3)
    # remove qrs from pts
    nn_pts_local = tf.subtract(nn_pts, nn_pts_center, name=tag + 'nn_pts_local')  # (N, P, K, 3)
    
    # Prepare features to be transformed
    # step 2, MLP
    nn_fts_from_pts_0 = pf.dense(nn_pts_local, C_pts_fts, tag + 'nn_fts_from_pts_0', is_training)
    nn_fts_from_pts = pf.dense(nn_fts_from_pts_0, C_pts_fts, tag + 'nn_fts_from_pts', is_training)
    # step 3, concat origin features with the new features
    if fts is None:
        nn_fts_input = nn_fts_from_pts
    else:
        nn_fts_from_prev = tf.gather_nd(fts, indices, name=tag + 'nn_fts_from_prev')
        nn_fts_input = tf.concat([nn_fts_from_pts, nn_fts_from_prev], axis=-1, name=tag + 'nn_fts_input')
    # step 4, according to the appendix, the second MLP is formes with two depthwise_conv layers 
    if with_X_transformation:
        ######################## X-transformation #########################
        X_0 = pf.conv2d(nn_pts_local, K * K, tag + 'X_0', is_training, (1, K))
        X_0_KK = tf.reshape(X_0, (N, P, K, K), name=tag + 'X_0_KK')
        X_1 = pf.depthwise_conv2d(X_0_KK, K, tag + 'X_1', is_training, (1, K))
        X_1_KK = tf.reshape(X_1, (N, P, K, K), name=tag + 'X_1_KK')
        X_2 = pf.depthwise_conv2d(X_1_KK, K, tag + 'X_2', is_training, (1, K), activation=None)
        X_2_KK = tf.reshape(X_2, (N, P, K, K), name=tag + 'X_2_KK')
        # step 5
        fts_X = tf.matmul(X_2_KK, nn_fts_input, name=tag + 'fts_X')
        ###################################################################
    else:
        fts_X = nn_fts_input
    # step 6
    fts_conv = pf.separable_conv2d(fts_X, C, tag + 'fts_conv', is_training, (1, K), depth_multiplier=depth_multiplier)
    fts_conv_3d = tf.squeeze(fts_conv, axis=2, name=tag + 'fts_conv_3d')
    
    if with_global:
        fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training, with_bn=False)
        fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global', is_training,  with_bn=False)
        #fts_global_0 = pf.dense(qrs, C // 4, tag + 'fts_global_0', is_training)
        #fts_global = pf.dense(fts_global_0, C // 4, tag + 'fts_global', is_training)
        return tf.concat([fts_global, fts_conv_3d], axis=-1, name=tag + 'fts_conv_3d_with_global')
    else:
        return fts_conv_3d
Exemple #20
0
def point_conv_transpose_2(pts_in,
                           fts_in,
                           pts_out,
                           fts_out_skipped,
                           indices,
                           K,
                           D,
                           C,
                           is_training,
                           activation=tf.nn.relu,
                           bn=True,
                           sorting_method='cxyz',
                           tag='point_conv'):
    '''
    pts_in: xyz points, Tensor, (batch_size, npoint, 3)
    fts_in: features, Tensor, (batch_size, npoint, channel_fts)
    pts_out: xyz points, Tensor, (batch_size, n_pts_out, 3), NOTE: pts_out is denser than pts.
    K: number of neighbors for one query, scalar, int
    D: dilation rate
    C: output feature channel number

    in the comments:
        N - batch size
        P - number of pts_in
        K - number of neighbors
    '''
    batch_size = pts_out.shape[0]
    n_pts_out = pts_out.shape[1]
    n_pts_in = pts_in.shape[1]

    if indices is None:
        _, indices_dilated = pf.knn_indices_general(pts_in, pts_out, K * D,
                                                    True)
        indices = indices_dilated[:, :, ::D, :]

        if sorting_method is not None:
            indices = pf.sort_points(pts_out, indices,
                                     sorting_method)  # (N, P, K, 2)

    # move the gathered pts_out to local frame
    nn_pts_out = tf.gather_nd(pts_out, indices,
                              name=tag + 'nn_pts_out')  # (N, P, K, 3)
    nn_pts_out_center = tf.expand_dims(pts_in,
                                       axis=2,
                                       name=tag +
                                       'nn_pts_out_center')  # (N, P, 1, 3)
    nn_pts_out_local = tf.subtract(nn_pts_out,
                                   nn_pts_out_center,
                                   name=tag +
                                   'nn_pts_out_local')  # (N, P, K, 3)

    if fts_in is None:
        nn_fts_input = pts_in  # (N, P, 3)
        #print('Error: fts_in is None.')
    else:
        #nn_fts_input = tf.concat([pts_in, fts_in], axis=-1, name=tag + 'nn_fts_input') # (N, P, 3+C_fts_in)
        nn_fts_input = fts_in  # (N, P, C_fts_in)

    nn_fts_input = tf.expand_dims(nn_fts_input, axis=2)  # (N, P, 1, C_fts_in)

    # TODO: maybe try other conv1d? fc? there
    fts_conv = pf.conv2d_transpose(
        nn_fts_input,
        C,
        tag + 'fts_deconv',
        is_training,
        kernel_size=(1, 1),
        strides=(1, K),
        with_bn=bn,
        activation=activation)  # (N, P, 1, C_fts_in) -> (N, P, K, C)

    if fts_out_skipped is not None:
        nn_fts_out_skipped = tf.gather_nd(fts_out_skipped,
                                          indices,
                                          name=tag + 'nn_fts_out_from_skip')
        fts_all_concat = tf.concat(
            [fts_conv, nn_fts_out_skipped, nn_pts_out_local],
            axis=-1)  # (N, P, K, C+3+channel_skipped)
    else:
        fts_all_concat = tf.concat([fts_conv, nn_pts_out_local],
                                   axis=-1)  # (N, P, K, C+3)

    fts_pts_conv = pf.conv2d(
        fts_all_concat,
        C,
        tag + 'fts_pts_conv1D',
        is_training,
        kernel_size=(1, 1),
        strides=(1, 1),
        with_bn=bn,
        activation=activation)  # (N, P, K, C+3_ch_skip?) -> (N, P, K, C)

    # summing up feature for each point
    fts_dense = tf.scatter_nd(tf.reshape(indices, (-1, 2)),
                              tf.reshape(fts_pts_conv, (-1, C)),
                              (batch_size, n_pts_out, C))  # (N, N_PTS_OUT, c)

    return fts_dense