-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #66 from eEcoLiDAR/feature_eigenvals
Feature eigenvals
- Loading branch information
Showing
6 changed files
with
151 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
laserchicken/feature_extractor/eigenvals_feature_extractor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import numpy as np | ||
|
||
from laserchicken import utils | ||
from laserchicken.feature_extractor.abc import AbstractFeatureExtractor | ||
|
||
|
||
def structure_tensor(points): | ||
""" | ||
Computes the structure tensor of points by computing the eigenvalues | ||
and eigenvectors of the covariance matrix of a point cloud. | ||
Parameters | ||
---------- | ||
points : (Mx3) array | ||
X, Y and Z coordinates of points. | ||
Returns | ||
------- | ||
eigenvalues : (1x3) array | ||
The eigenvalues corresponding to the eigenvectors of the covariance | ||
matrix. | ||
eigenvectors : (3,3) array | ||
The eigenvectors of the covariance matrix. | ||
""" | ||
if points.shape[0] > 3: | ||
cov_mat = np.cov(points, rowvar=False) | ||
eigenvalues, eigenvectors = np.linalg.eig(cov_mat) | ||
order = np.argsort(-eigenvalues) | ||
eigenvalues = eigenvalues[order] | ||
eigenvectors = eigenvectors[:, order] | ||
return eigenvalues, eigenvectors | ||
else: | ||
raise ValueError('Not enough points to compute eigenvalues/vectors.') | ||
|
||
|
||
class EigenValueFeatureExtractor(AbstractFeatureExtractor): | ||
@classmethod | ||
def requires(cls): | ||
return [] | ||
|
||
@classmethod | ||
def provides(cls): | ||
return ['eigenv_1', 'eigenv_2', 'eigenv_3'] | ||
|
||
def extract(self, sourcepc, neighborhood, targetpc, targetindex): | ||
nbptsX, nbptsY, nbptsZ = utils.get_point(sourcepc, neighborhood) | ||
matrix = np.column_stack((nbptsX, nbptsY, nbptsZ)) | ||
eigenvals, eigenvecs = structure_tensor(matrix) | ||
return [eigenvals[0], eigenvals[1], eigenvals[2]] |
36 changes: 36 additions & 0 deletions
36
laserchicken/test_feature_extractor/test_eigenvals_feature_extractor.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import os | ||
import random | ||
import unittest | ||
|
||
from laserchicken import compute_neighbors, feature_extractor, keys, read_las, utils | ||
|
||
|
||
class TestExtractEigenValues(unittest.TestCase): | ||
|
||
_test_file_name = 'AHN3.las' | ||
_test_data_source = 'testdata' | ||
pointcloud = None | ||
|
||
def test_eigenvalues_in_cylinders(self): | ||
"""Test computing of eigenvalues in cylinder.""" | ||
num_all_pc_points = len(self.pointcloud[keys.point]["x"]["data"]) | ||
rand_indices = [random.randint(0, num_all_pc_points) for p in range(20)] | ||
target_pointcloud = utils.copy_pointcloud(self.pointcloud, rand_indices) | ||
numtargets = len(target_pointcloud[keys.point]["x"]["data"]) | ||
radius = 2.5 | ||
result_index_lists = compute_neighbors.compute_cylinder_neighbourhood_indicies( | ||
self.pointcloud, target_pointcloud, radius) | ||
feature_extractor.compute_features(self.pointcloud, result_index_lists, target_pointcloud, | ||
["eigenv_1", "eigenv_2", "eigenv_3"]) | ||
for i in range(numtargets): | ||
lambda1, lambda2, lambda3 = utils.get_features(target_pointcloud, i, ["eigenv_1", "eigenv_2", "eigenv_3"]) | ||
self.assertTrue(lambda1 >= lambda2 and lambda2 >= lambda3) | ||
self.assertEqual("laserchicken.feature_extractor.eigenvals_feature_extractor", | ||
target_pointcloud[keys.provenance][0]["module"]) | ||
|
||
def setUp(self): | ||
self.pointcloud = read_las.read(os.path.join(self._test_data_source, self._test_file_name)) | ||
random.seed(102938482634) | ||
|
||
def tearDown(self): | ||
pass |
43 changes: 26 additions & 17 deletions
43
laserchicken/test_feature_extractor/test_extract_features.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,51 @@ | ||
"""Test feature extraction.""" | ||
import pytest | ||
import numpy as np | ||
from laserchicken import feature_extractor,keys,test_tools | ||
import pytest | ||
|
||
from laserchicken import feature_extractor, keys, test_tools | ||
|
||
from . import __name__ as test_module_name | ||
|
||
# Overwrite the available feature extractors with test feature extractors | ||
feature_extractor.FEATURES = feature_extractor._feature_map(test_module_name) | ||
|
||
@pytest.fixture(scope='module', autouse=True) | ||
def override_features(): | ||
"""Overwrite the available feature extractors with test feature extractors.""" | ||
feature_extractor.FEATURES = feature_extractor._feature_map(test_module_name) | ||
yield | ||
feature_extractor.FEATURES = feature_extractor._feature_map(feature_extractor.__name__) | ||
|
||
|
||
def _compute_features(target,feature_names,overwrite = False): | ||
def _compute_features(target, feature_names, overwrite=False): | ||
neighborhoods = [[] for i in range(len(target["vertex"]["x"]["data"]))] | ||
feature_extractor.compute_features({}, neighborhoods, target, feature_names, overwrite) | ||
return target | ||
|
||
|
||
def test_extract_single_feature(): | ||
target = test_tools.ComplexTestData.get_point_cloud() | ||
_compute_features(target,['test3_a']) | ||
assert ('test1_b' in target[keys.point]) | ||
_compute_features(target, ['test3_a']) | ||
assert 'test1_b' in target[keys.point] | ||
assert all(target[keys.point]['test3_a']['data'] == target[keys.point]['z']['data']) | ||
|
||
|
||
def test_extract_multiple_features(): | ||
target = test_tools.ComplexTestData.get_point_cloud() | ||
feature_names = ['test3_a','test2_b'] | ||
target = _compute_features(target,feature_names) | ||
feature_names = ['test3_a', 'test2_b'] | ||
target = _compute_features(target, feature_names) | ||
assert ('test3_a' in target[keys.point] and 'test2_b' in target[keys.point]) | ||
|
||
|
||
def test_extract_does_not_overwrite(): | ||
target = test_tools.ComplexTestData.get_point_cloud() | ||
target[keys.point]['test2_b'] = {"type":np.float64,"data":[0.9,0.99,0.999,0.9999]} | ||
feature_names = ['test3_a','test2_b'] | ||
target = _compute_features(target,feature_names) | ||
assert (target[keys.point]['test2_b']['data'][2] == 0.999) | ||
target[keys.point]['test2_b'] = {"type": np.float64, "data": [0.9, 0.99, 0.999, 0.9999]} | ||
feature_names = ['test3_a', 'test2_b'] | ||
target = _compute_features(target, feature_names) | ||
assert target[keys.point]['test2_b']['data'][2] == 0.999 | ||
|
||
|
||
def test_extract_can_overwrite(): | ||
target = test_tools.ComplexTestData.get_point_cloud() | ||
target[keys.point]['test2_b'] = {"type":np.float64,"data":[0.9,0.99,0.999,0.9999]} | ||
feature_names = ['test3_a','test2_b'] | ||
target = _compute_features(target,feature_names,overwrite = True) | ||
assert (target[keys.point]['test2_b']['data'][2] == 11.5) | ||
target[keys.point]['test2_b'] = {"type": np.float64, "data": [0.9, 0.99, 0.999, 0.9999]} | ||
feature_names = ['test3_a', 'test2_b'] | ||
target = _compute_features(target, feature_names, overwrite=True) | ||
assert target[keys.point]['test2_b']['data'][2] == 11.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters