MVA-2021 / npm3d / hw3_descriptors / code / descriptors.py
descriptors.py
Raw
#
#
#      0=============================0
#      |    TP4 Point Descriptors    |
#      0=============================0
#
#
# ------------------------------------------------------------------------------------------
#
#      Script of the practical session
#
# ------------------------------------------------------------------------------------------
#
#      Hugues THOMAS - 13/12/2017
#


import numpy as np
from matplotlib import pyplot as plt
from sklearn.neighbors import KDTree
from ply import write_ply, read_ply
import time

import matplotlib.pyplot as plt

# ------------------------------------------------------------------------------------------
#
#           Functions
#       \***************/
#
#



def PCA(points):

    # compute centroid
    centroid = points.mean(axis=0)
    # compute centered PC
    points = points - centroid[None, :]
    # compute covariance matrix
    cov = 1/points.shape[0] * points.T @ points  
    
    # return eigenvalues and eigenvectors
    return np.linalg.eigh(cov)


def compute_local_PCA(query_points, cloud_points, radius, k=-1):

    # This function needs to compute PCA on the neighborhoods of all query_points in cloud_points

    # create a kdtree for more efficient queries
    tree = KDTree(cloud_points)

    # query the neighbors for each point in query_points
    if k<=0: # radius method
        neighbors_list = tree.query_radius(query_points, radius)
    else: # k-nearest-neighbors method 
        neighbors_list = tree.query(query_points, k, return_distance=False)

    all_eigenvalues = []
    all_eigenvectors = []

    # lengths = []
    for neighbors in neighbors_list:
        # lengths.append(len(neighbors))
        eigvals, eigvects = PCA(cloud_points[neighbors])
        all_eigenvalues.append(eigvals)
        all_eigenvectors.append(eigvects)

    # plt.hist(lengths, 100, density=True, facecolor='g', alpha=0.75)
    # plt.show()

    all_eigenvalues = np.stack(all_eigenvalues)
    all_eigenvectors = np.stack(all_eigenvectors)

    return all_eigenvalues, all_eigenvectors


def compute_features(query_points, cloud_points, radius):

    # compute eigenvalues and eigenvectors
    eigvals, eigvects = compute_local_PCA(query_points, cloud_points, radius)

    # compute the features
    eps = 1e-8
    normals = eigvects[:, :, 0]
    verticality = 2 * np.arcsin(np.abs(normals[:, 2])) / np.pi
    linearity = 1 - eigvals[:, 1] / (eigvals[:, 2]+eps)
    planarity = (eigvals[:, 1] - eigvals[:, 0]) / (eigvals[:, 2]+eps)
    sphericity = eigvals[:, 0] / (eigvals[:, 2]+eps)

    return verticality, linearity, planarity, sphericity


# ------------------------------------------------------------------------------------------
#
#           Main
#       \**********/
#
# 
#   Here you can define the instructions that are called when you execute this file
#

if __name__ == '__main__':

    # PCA verification
    # ****************
    if False:

        # Load cloud as a [N x 3] matrix
        cloud_path = '../data/Lille_street_small.ply'
        cloud_ply = read_ply(cloud_path)
        cloud = np.vstack((cloud_ply['x'], cloud_ply['y'], cloud_ply['z'])).T

        # Compute PCA on the whole cloud
        eigenvalues, eigenvectors = PCA(cloud)

        # Print your result
        print(eigenvalues)

        # Expected values :
        #
        #   [lambda_3; lambda_2; lambda_1] = [ 5.25050177 21.7893201  89.58924003]
        #
        #   (the convention is always lambda_1 >= lambda_2 >= lambda_3)
        #

		
    # Normal computation
    # ******************
    if False:

        # Load cloud as a [N x 3] matrix
        cloud_path = '../data/Lille_street_small.ply'
        cloud_ply = read_ply(cloud_path)
        cloud = np.vstack((cloud_ply['x'], cloud_ply['y'], cloud_ply['z'])).T

        # Compute PCA on the whole cloud
        all_eigenvalues, all_eigenvectors = compute_local_PCA(cloud, cloud, 0.50)
        # all_eigenvalues, all_eigenvectors = compute_local_PCA(cloud, cloud, 0.50, k=30)
        normals = all_eigenvectors[:, :, 0]

        # Save cloud with normals
        write_ply('../Lille_street_small_normals.ply', (cloud, normals), ['x', 'y', 'z', 'nx', 'ny', 'nz'])


    # Features computation
    # ******************
    if True:

        # Load cloud as a [N x 3] matrix
        cloud_path = '../data/Lille_street_small.ply'
        cloud_ply = read_ply(cloud_path)
        cloud = np.vstack((cloud_ply['x'], cloud_ply['y'], cloud_ply['z'])).T

        # Compute features on the whole cloud
        verticality, linearity, planarity, sphericity = compute_features(cloud, cloud, 0.50)

        # Save cloud with features
        write_ply('../Lille_street_small_features.ply', [cloud, verticality, linearity, planarity, sphericity], ['x', 'y', 'z', 'v', 'l', 'p', 's'])