diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f312ef..42cf608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.3.2 - 2019-11- +## Added +- Features: + 'max_intensity', 'min_intensity', 'range_intensity', 'mean_intensity', 'std_intensity', 'coeff_var_intensity' + +## Changed +- Features renamed','median_norm_z' + 'max_norm_z', 'min_norm_z', 'range_norm_z', 'mean_norm_z', 'std_norm_z', 'coeff_var_norm_z', 'density_absolute_mean_norm_z', 'entropy_norm_z','kurto_norm_z', 'skew_norm_z','var_z', 'perc_1_norm_z', 'perc_100_norm_z' + 'min_normalized_height', 'max_normalized_height', 'range_normalized_height', 'mean_normalized_height','std_normalized_height', 'coeff_var_normalized_height', 'density_absolute_mean_normalized_height','entropy_normalized_height','kurto_normalized_height','median_normalized_height', 'skew_normalized_height','var_normalized_height', 'perc_1_normalized_height', 'perc_100_normalized_height' + + ## 0.3.1 - 2019-09-25 ## Added - Percentiles 1-100 diff --git a/laserchicken/feature_extractor/density_absolute_mean_z_feature_extractor.py b/laserchicken/feature_extractor/density_absolute_mean_feature_extractor.py similarity index 88% rename from laserchicken/feature_extractor/density_absolute_mean_z_feature_extractor.py rename to laserchicken/feature_extractor/density_absolute_mean_feature_extractor.py index 1b7e97d..797ad48 100644 --- a/laserchicken/feature_extractor/density_absolute_mean_z_feature_extractor.py +++ b/laserchicken/feature_extractor/density_absolute_mean_feature_extractor.py @@ -17,9 +17,10 @@ def _is_ground(i, point_cloud): return point_cloud[point]['raw_classification']["data"][i] in GROUND_TAGS -class DensityAbsoluteMeanZFeatureExtractor(FeatureExtractor): +class DensityAbsoluteMeanFeatureExtractor(FeatureExtractor): """Feature extractor for the point density.""" - DATA_KEY = 'z' + def __init__(self, data_key='z'): + self.data_key = data_key @classmethod def requires(cls): @@ -33,8 +34,7 @@ def requires(cls): """ return [] - @classmethod - def provides(cls): + def provides(self): """ Get a list of names of the feature values. @@ -44,7 +44,7 @@ def provides(cls): :return: List of feature names """ - return ['density_absolute_mean_z'] + return ['density_absolute_mean_' + self.data_key] def extract(self, point_cloud, neighborhood, target_point_cloud, target_index, volume_description): """ @@ -76,12 +76,12 @@ def _get_ground_indices(point_cloud, ground_tags): def _get_density_absolute_mean(self, non_ground_indices, source_point_cloud): n_non_ground = len(non_ground_indices) - z_non_ground = source_point_cloud[point][self.DATA_KEY]["data"][non_ground_indices] + data_non_ground = source_point_cloud[point][self.data_key]["data"][non_ground_indices] if n_non_ground == 0: density_absolute_mean = 0. else: density_absolute_mean = float( - len(z_non_ground[z_non_ground > np.mean(z_non_ground)])) / n_non_ground * 100. + len(data_non_ground[data_non_ground > np.mean(data_non_ground)])) / n_non_ground * 100. return density_absolute_mean def get_params(self): diff --git a/laserchicken/feature_extractor/density_absolute_mean_norm_z_feature_extractor.py b/laserchicken/feature_extractor/density_absolute_mean_norm_z_feature_extractor.py deleted file mode 100644 index e44e111..0000000 --- a/laserchicken/feature_extractor/density_absolute_mean_norm_z_feature_extractor.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Pulse penetration ratio and density absolute mean calculations. - -See https://github.com/eEcoLiDAR/eEcoLiDAR/issues/23. -""" - -import numpy as np - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.feature_extractor.density_absolute_mean_z_feature_extractor import \ - DensityAbsoluteMeanZFeatureExtractor -from laserchicken.keys import point, normalized_height - -# classification according to -# http://www.asprs.org/wp-content/uploads/2010/12/LAS_1-4_R6.pdf -GROUND_TAGS = [2] - - -class DensityAbsoluteMeanNormZFeatureExtractor(DensityAbsoluteMeanZFeatureExtractor): - """Feature extractor for the point density.""" - DATA_KEY = normalized_height - - @classmethod - def provides(cls): - """ - Get a list of names of the feature values. - - This will return as many names as the number feature values that will be returned. - For instance, if a feature extractor returns the first 3 eigen values, this method - should return 3 names, for instance 'eigen_value_1', 'eigen_value_2' and 'eigen_value_3'. - - :return: List of feature names - """ - return ['density_absolute_mean_norm_z'] diff --git a/laserchicken/feature_extractor/entropy_feature_extractor.py b/laserchicken/feature_extractor/entropy_feature_extractor.py new file mode 100644 index 0000000..3fbb397 --- /dev/null +++ b/laserchicken/feature_extractor/entropy_feature_extractor.py @@ -0,0 +1,47 @@ +"""Shannan entropy calculation. For more info see https://rdrr.io/cran/lidR/man/entropy.html""" + +import numpy as np +from laserchicken import keys +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor + + +class EntropyFeatureExtractor(FeatureExtractor): + layer_thickness = 0.5 + min_val = None + max_val = None + + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + return ['entropy_' + self.data_key] + + def get_params(self): + p = [self.layer_thickness] + if self.min_val is not None: + p.append(self.min_val) + if self.max_val is not None: + p.append(self.max_val) + return p + + def extract(self, source_pc, neighborhood, target_pc, target_index, volume_description): + if len(neighborhood) == 0: + return 0 + source_data = source_pc[keys.point][self.data_key]["data"][neighborhood] + data_min = np.min(source_data) if self.min_val is None else self.min_val + data_max = np.max(source_data) if self.max_val is None else self.max_val + if data_min == data_max: + return 0 + n_bins = int(np.ceil((data_max - data_min) / self.layer_thickness)) + data = np.histogram(source_data, bins=n_bins, range=(data_min, data_max), density=True)[0] + entropy_func = np.vectorize(_x_log_2x) + norm = np.sum(data) + return -(entropy_func(data / norm)).sum() + + +def _x_log_2x(x): + return 0 if x == 0 else x * np.log2(x) diff --git a/laserchicken/feature_extractor/entropy_norm_z_feature_extractor.py b/laserchicken/feature_extractor/entropy_norm_z_feature_extractor.py deleted file mode 100644 index 708ef89..0000000 --- a/laserchicken/feature_extractor/entropy_norm_z_feature_extractor.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Shannan entropy calculation. For more info see https://rdrr.io/cran/lidR/man/entropy.html""" - -from laserchicken import keys -from laserchicken.feature_extractor.entropy_z_feature_extractor import EntropyZFeatureExtractor - - -class EntropyNormZFeatureExtractor(EntropyZFeatureExtractor): - DATA_KEY = keys.normalized_height - - @classmethod - def provides(cls): - return ['entropy_norm_z'] diff --git a/laserchicken/feature_extractor/entropy_z_feature_extractor.py b/laserchicken/feature_extractor/entropy_z_feature_extractor.py deleted file mode 100644 index 078435d..0000000 --- a/laserchicken/feature_extractor/entropy_z_feature_extractor.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Shannan entropy calculation. For more info see https://rdrr.io/cran/lidR/man/entropy.html""" - -import numpy as np -from laserchicken import keys -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor - - -class EntropyZFeatureExtractor(FeatureExtractor): - # TODO: make this settable from command line - layer_thickness = 0.5 - z_min = None - z_max = None - - DATA_KEY = "z" - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['entropy_z'] - - def get_params(self): - p = [self.layer_thickness] - if self.z_min is not None: - p.append(self.z_min) - if self.z_max is not None: - p.append(self.z_max) - return p - - def extract(self, source_pc, neighborhood, target_pc, target_index, volume_description): - if len(neighborhood) == 0: - return 0 - z = source_pc[keys.point][self.DATA_KEY]["data"][neighborhood] - _z_min = np.min(z) if self.z_min is None else self.z_min - _z_max = np.max(z) if self.z_max is None else self.z_max - if _z_min == _z_max: - return 0 - n_bins = int(np.ceil((_z_max - _z_min) / self.layer_thickness)) - data = np.histogram(z, bins=n_bins, range=(_z_min, _z_max), density=True)[0] - entropy_func = np.vectorize(_x_log_2x) - norm = np.sum(data) - return -(entropy_func(data / norm)).sum() - - -def _x_log_2x(x): - return 0 if x == 0 else x * np.log2(x) diff --git a/laserchicken/feature_extractor/feature_map.py b/laserchicken/feature_extractor/feature_map.py index 7b9d1f4..35be0c5 100644 --- a/laserchicken/feature_extractor/feature_map.py +++ b/laserchicken/feature_extractor/feature_map.py @@ -1,28 +1,19 @@ from laserchicken import keys from laserchicken.feature_extractor.band_ratio_feature_extractor import BandRatioFeatureExtractor -from .density_absolute_mean_norm_z_feature_extractor import DensityAbsoluteMeanNormZFeatureExtractor -from .density_absolute_mean_z_feature_extractor import DensityAbsoluteMeanZFeatureExtractor +from .density_absolute_mean_feature_extractor import DensityAbsoluteMeanFeatureExtractor from .density_feature_extractor import PointDensityFeatureExtractor from .echo_ratio_feature_extractor import EchoRatioFeatureExtractor from .eigenvals_feature_extractor import EigenValueVectorizeFeatureExtractor -from .entropy_norm_z_feature_extractor import EntropyNormZFeatureExtractor -from .entropy_z_feature_extractor import EntropyZFeatureExtractor -from .kurtosis_norm_z_feature_extractor import KurtosisNormZFeatureExtractor -from .kurtosis_z_feature_extractor import KurtosisZFeatureExtractor -from .mean_std_coeff_norm_z_feature_extractor import MeanStdCoeffNormZFeatureExtractor -from .mean_std_coeff_z_feature_extractor import MeanStdCoeffZFeatureExtractor -from .median_norm_z_feature_extractor import MedianNormZFeatureExtractor -from .median_z_feature_extractor import MedianZFeatureExtractor -from .percentile_norm_z_feature_extractor import PercentileNormZFeatureExtractor -from .percentile_z_feature_extractor import PercentileZFeatureExtractor +from .entropy_feature_extractor import EntropyFeatureExtractor +from .kurtosis_feature_extractor import KurtosisFeatureExtractor +from .mean_std_coeff_feature_extractor import MeanStdCoeffFeatureExtractor +from .median_feature_extractor import MedianFeatureExtractor +from .percentile_feature_extractor import PercentileFeatureExtractor from .pulse_penetration_feature_extractor import PulsePenetrationFeatureExtractor -from .range_norm_z_feature_extractor import RangeNormZFeatureExtractor -from .range_z_feature_extractor import RangeZFeatureExtractor +from .range_feature_extractor import RangeFeatureExtractor from .sigma_z_feature_extractor import SigmaZFeatureExtractor -from .skew_norm_z_feature_extractor import SkewNormZFeatureExtractor -from .skew_z_feature_extractor import SkewZFeatureExtractor -from .var_norm_z_feature_extractor import VarianceNormZFeatureExtractor -from .var_z_feature_extractor import VarianceZFeatureExtractor +from .skew_feature_extractor import SkewFeatureExtractor +from .var_feature_extractor import VarianceFeatureExtractor def create_default_feature_map(): @@ -45,29 +36,29 @@ def _get_default_extractors(): return [PointDensityFeatureExtractor(), EchoRatioFeatureExtractor(), EigenValueVectorizeFeatureExtractor(), - EntropyZFeatureExtractor(), - PercentileZFeatureExtractor(), + EntropyFeatureExtractor(), + EntropyFeatureExtractor(data_key=keys.normalized_height), PulsePenetrationFeatureExtractor(), SigmaZFeatureExtractor(), - MedianZFeatureExtractor(), - RangeZFeatureExtractor(), - VarianceZFeatureExtractor(), - MeanStdCoeffZFeatureExtractor(), - SkewZFeatureExtractor(), - KurtosisZFeatureExtractor(), - SkewNormZFeatureExtractor(), - MeanStdCoeffNormZFeatureExtractor(), - VarianceNormZFeatureExtractor(), - RangeNormZFeatureExtractor(), - KurtosisNormZFeatureExtractor(), - EntropyNormZFeatureExtractor(), - MedianNormZFeatureExtractor(), - PercentileNormZFeatureExtractor(), - DensityAbsoluteMeanZFeatureExtractor(), - DensityAbsoluteMeanNormZFeatureExtractor(), + MedianFeatureExtractor(), + MedianFeatureExtractor(data_key=keys.normalized_height), + VarianceFeatureExtractor(), + VarianceFeatureExtractor(data_key=keys.normalized_height), + MeanStdCoeffFeatureExtractor(), + MeanStdCoeffFeatureExtractor(data_key=keys.normalized_height), + MeanStdCoeffFeatureExtractor(data_key=keys.intensity), + SkewFeatureExtractor(), + SkewFeatureExtractor(data_key=keys.normalized_height), + KurtosisFeatureExtractor(), + KurtosisFeatureExtractor(data_key=keys.normalized_height), + RangeFeatureExtractor(), + RangeFeatureExtractor(data_key=keys.normalized_height), + RangeFeatureExtractor(data_key=keys.intensity), + DensityAbsoluteMeanFeatureExtractor(), + DensityAbsoluteMeanFeatureExtractor(data_key=keys.normalized_height), BandRatioFeatureExtractor(None, 1, data_key=keys.normalized_height), BandRatioFeatureExtractor(1, 2, data_key=keys.normalized_height), BandRatioFeatureExtractor(2, 3, data_key=keys.normalized_height), BandRatioFeatureExtractor(3, None, data_key=keys.normalized_height)] \ - + [PercentileZFeatureExtractor(p) for p in range(1, 101)] \ - + [PercentileNormZFeatureExtractor(p) for p in range(1, 101)] + + [PercentileFeatureExtractor(percentile=p) for p in range(1, 101)] \ + + [PercentileFeatureExtractor(percentile=p, data_key=keys.normalized_height) for p in range(1, 101)] diff --git a/laserchicken/feature_extractor/kurtosis_z_feature_extractor.py b/laserchicken/feature_extractor/kurtosis_feature_extractor.py similarity index 67% rename from laserchicken/feature_extractor/kurtosis_z_feature_extractor.py rename to laserchicken/feature_extractor/kurtosis_feature_extractor.py index a23c659..c43c146 100644 --- a/laserchicken/feature_extractor/kurtosis_z_feature_extractor.py +++ b/laserchicken/feature_extractor/kurtosis_feature_extractor.py @@ -1,26 +1,26 @@ -import numpy as np -import scipy.stats.stats as stat - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class KurtosisZFeatureExtractor(FeatureExtractor): - """Calculates the variation on the z axis.""" - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['kurto_z'] - - def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): - if neighborhood: - z = sourcepc[point][self.DATA_KEY]['data'][neighborhood] - kurtosis_z = stat.kurtosis(z) - else: - kurtosis_z = np.NaN - return kurtosis_z +import numpy as np +import scipy.stats.stats as stat + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class KurtosisFeatureExtractor(FeatureExtractor): + """Calculates the variation on the z axis.""" + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + return ['kurto_' + self.data_key] + + def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): + if neighborhood: + z = sourcepc[point][self.data_key]['data'][neighborhood] + kurtosis_z = stat.kurtosis(z) + else: + kurtosis_z = np.NaN + return kurtosis_z diff --git a/laserchicken/feature_extractor/kurtosis_norm_z_feature_extractor.py b/laserchicken/feature_extractor/kurtosis_norm_z_feature_extractor.py deleted file mode 100644 index 01d7588..0000000 --- a/laserchicken/feature_extractor/kurtosis_norm_z_feature_extractor.py +++ /dev/null @@ -1,11 +0,0 @@ -from laserchicken import keys -from laserchicken.feature_extractor.kurtosis_z_feature_extractor import KurtosisZFeatureExtractor - - -class KurtosisNormZFeatureExtractor(KurtosisZFeatureExtractor): - """Calculates the variation on the normalized height.""" - DATA_KEY = keys.normalized_height - - @classmethod - def provides(cls): - return ['kurto_norm_z'] diff --git a/laserchicken/feature_extractor/mean_std_coeff_z_feature_extractor.py b/laserchicken/feature_extractor/mean_std_coeff_feature_extractor.py similarity index 64% rename from laserchicken/feature_extractor/mean_std_coeff_z_feature_extractor.py rename to laserchicken/feature_extractor/mean_std_coeff_feature_extractor.py index 3d81705..8fd424b 100644 --- a/laserchicken/feature_extractor/mean_std_coeff_z_feature_extractor.py +++ b/laserchicken/feature_extractor/mean_std_coeff_feature_extractor.py @@ -1,27 +1,28 @@ -import numpy as np - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class MeanStdCoeffZFeatureExtractor(FeatureExtractor): - """Calculates mean, standard deviation and the ratio between the two.""" - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['mean_z', 'std_z', 'coeff_var_z'] - - def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): - if neighborhood: - z = sourcepc[point][self.DATA_KEY]['data'][neighborhood] - mean_z = np.mean(z) - std_z = np.std(z) - coeff_var_z = std_z / mean_z - else: - mean_z = std_z = coeff_var_z = np.NaN - return mean_z, std_z, coeff_var_z +import numpy as np + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class MeanStdCoeffFeatureExtractor(FeatureExtractor): + """Calculates mean, standard deviation and the ratio between the two.""" + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + base_names = ['mean_', 'std_', 'coeff_var_'] + return [base + str(self.data_key) for base in base_names] + + def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): + if neighborhood: + z = sourcepc[point][self.data_key]['data'][neighborhood] + mean_z = np.mean(z) + std_z = np.std(z) + coeff_var_z = std_z / mean_z + else: + mean_z = std_z = coeff_var_z = np.NaN + return mean_z, std_z, coeff_var_z diff --git a/laserchicken/feature_extractor/mean_std_coeff_norm_z_feature_extractor.py b/laserchicken/feature_extractor/mean_std_coeff_norm_z_feature_extractor.py deleted file mode 100644 index 209aa1e..0000000 --- a/laserchicken/feature_extractor/mean_std_coeff_norm_z_feature_extractor.py +++ /dev/null @@ -1,11 +0,0 @@ -from laserchicken import keys -from laserchicken.feature_extractor.mean_std_coeff_z_feature_extractor import MeanStdCoeffZFeatureExtractor - - -class MeanStdCoeffNormZFeatureExtractor(MeanStdCoeffZFeatureExtractor): - """Calculates mean, standard deviation of the normalized height and the ratio between the two.""" - DATA_KEY = keys.normalized_height - - @classmethod - def provides(cls): - return ['mean_norm_z', 'std_norm_z', 'coeff_var_norm_z'] diff --git a/laserchicken/feature_extractor/median_z_feature_extractor.py b/laserchicken/feature_extractor/median_feature_extractor.py similarity index 51% rename from laserchicken/feature_extractor/median_z_feature_extractor.py rename to laserchicken/feature_extractor/median_feature_extractor.py index cc03415..5951a24 100644 --- a/laserchicken/feature_extractor/median_z_feature_extractor.py +++ b/laserchicken/feature_extractor/median_feature_extractor.py @@ -1,25 +1,25 @@ -import numpy as np - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class MedianZFeatureExtractor(FeatureExtractor): - """Calculates the median on the z axis.""" - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['median_z'] - - def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): - if neighborhood: - z = sourcepc[point][self.DATA_KEY]['data'][neighborhood] - median_z = np.median(z) - else: - median_z = np.NaN - return median_z +import numpy as np + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class MedianFeatureExtractor(FeatureExtractor): + """Calculates the median on the z axis.""" + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + return ['median_' + self.data_key] + + def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): + if neighborhood: + source_data = sourcepc[point][self.data_key]['data'][neighborhood] + median = np.median(source_data) + else: + median = np.NaN + return median diff --git a/laserchicken/feature_extractor/median_norm_z_feature_extractor.py b/laserchicken/feature_extractor/median_norm_z_feature_extractor.py deleted file mode 100644 index f8744be..0000000 --- a/laserchicken/feature_extractor/median_norm_z_feature_extractor.py +++ /dev/null @@ -1,11 +0,0 @@ -from laserchicken.feature_extractor.median_z_feature_extractor import MedianZFeatureExtractor -from laserchicken.keys import normalized_height - - -class MedianNormZFeatureExtractor(MedianZFeatureExtractor): - """Calculates the median on the normalized height.""" - DATA_KEY = normalized_height - - @classmethod - def provides(cls): - return ['median_norm_z'] diff --git a/laserchicken/feature_extractor/percentile_z_feature_extractor.py b/laserchicken/feature_extractor/percentile_feature_extractor.py similarity index 85% rename from laserchicken/feature_extractor/percentile_z_feature_extractor.py rename to laserchicken/feature_extractor/percentile_feature_extractor.py index 530fde9..8f55ce1 100644 --- a/laserchicken/feature_extractor/percentile_z_feature_extractor.py +++ b/laserchicken/feature_extractor/percentile_feature_extractor.py @@ -4,12 +4,11 @@ from laserchicken.keys import point -class PercentileZFeatureExtractor(FeatureExtractor): +class PercentileFeatureExtractor(FeatureExtractor): """Height percentiles feature extractor class.""" - DATA_KEY = 'z' - - def __init__(self, percentile=50): + def __init__(self, percentile=50, data_key='z'): self.percentile = percentile + self.data_key = data_key @classmethod def requires(cls): @@ -36,7 +35,7 @@ def provides(self): return [self.generate_feature_name(self.percentile)] def generate_feature_name(self, percentile): - return 'perc_{}_{}'.format(percentile, self.DATA_KEY) + return 'perc_{}_{}'.format(percentile, self.data_key) def extract(self, point_cloud, neighborhood, target_point_cloud, target_index, volume_description): """ @@ -49,8 +48,8 @@ def extract(self, point_cloud, neighborhood, target_point_cloud, target_index, v :target_index: index of the target point in the target point cloud :return: feature value """ - z = point_cloud[point][self.DATA_KEY]['data'][neighborhood] - return stats.scoreatpercentile(z, self.percentile) + source_data = point_cloud[point][self.data_key]['data'][neighborhood] + return stats.scoreatpercentile(source_data, self.percentile) def get_params(self): """ diff --git a/laserchicken/feature_extractor/percentile_norm_z_feature_extractor.py b/laserchicken/feature_extractor/percentile_norm_z_feature_extractor.py deleted file mode 100644 index 0ab9d32..0000000 --- a/laserchicken/feature_extractor/percentile_norm_z_feature_extractor.py +++ /dev/null @@ -1,7 +0,0 @@ -from laserchicken.feature_extractor.percentile_z_feature_extractor import PercentileZFeatureExtractor -from laserchicken.keys import normalized_height - - -class PercentileNormZFeatureExtractor(PercentileZFeatureExtractor): - """Normalized height percentiles for feature extractor class.""" - DATA_KEY = normalized_height diff --git a/laserchicken/feature_extractor/range_z_feature_extractor.py b/laserchicken/feature_extractor/range_feature_extractor.py similarity index 53% rename from laserchicken/feature_extractor/range_z_feature_extractor.py rename to laserchicken/feature_extractor/range_feature_extractor.py index eee5532..4afe84d 100644 --- a/laserchicken/feature_extractor/range_z_feature_extractor.py +++ b/laserchicken/feature_extractor/range_feature_extractor.py @@ -1,30 +1,32 @@ -import numpy as np - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class RangeZFeatureExtractor(FeatureExtractor): - """Calculates the max, min and range on the z axis.""" - - DEFAULT_MAX = float('NaN') - DEFAULT_MIN = float('NaN') - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['max_z', 'min_z', 'range_z'] - - def extract(self, source_point_cloud, neighborhood, target_point_cloud, target_index, volume_description): - if neighborhood: - z = source_point_cloud[point][self.DATA_KEY]['data'][neighborhood] - max_z = np.max(z) if len(z) > 0 else self.DEFAULT_MAX - min_z = np.min(z) if len(z) > 0 else self.DEFAULT_MIN - range_z = max_z - min_z - else: - max_z = min_z = range_z = np.NaN - return max_z, min_z, range_z +import numpy as np + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class RangeFeatureExtractor(FeatureExtractor): + """Calculates the max, min and range on the z axis.""" + + DEFAULT_MAX = float('NaN') + DEFAULT_MIN = float('NaN') + + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + base_names = ['max_', 'min_', 'range_'] + return [base + str(self.data_key) for base in base_names] + + def extract(self, source_point_cloud, neighborhood, target_point_cloud, target_index, volume_description): + if neighborhood: + source_data = source_point_cloud[point][self.data_key]['data'][neighborhood] + max_z = np.max(source_data) if len(source_data) > 0 else self.DEFAULT_MAX + min_z = np.min(source_data) if len(source_data) > 0 else self.DEFAULT_MIN + range_z = max_z - min_z + else: + max_z = min_z = range_z = np.NaN + return max_z, min_z, range_z diff --git a/laserchicken/feature_extractor/range_norm_z_feature_extractor.py b/laserchicken/feature_extractor/range_norm_z_feature_extractor.py deleted file mode 100644 index 9e6fcd7..0000000 --- a/laserchicken/feature_extractor/range_norm_z_feature_extractor.py +++ /dev/null @@ -1,12 +0,0 @@ -from laserchicken.feature_extractor.test_range_z_feature_extractor import RangeZFeatureExtractor -from laserchicken.keys import normalized_height - - -class RangeNormZFeatureExtractor(RangeZFeatureExtractor): - """Calculates the max, min and range and max on the normalized z axis.""" - - DATA_KEY = normalized_height - - @classmethod - def provides(cls): - return ['max_norm_z', 'min_norm_z', 'range_norm_z'] diff --git a/laserchicken/feature_extractor/skew_z_feature_extractor.py b/laserchicken/feature_extractor/skew_feature_extractor.py similarity index 53% rename from laserchicken/feature_extractor/skew_z_feature_extractor.py rename to laserchicken/feature_extractor/skew_feature_extractor.py index 111d9bb..b76391c 100644 --- a/laserchicken/feature_extractor/skew_z_feature_extractor.py +++ b/laserchicken/feature_extractor/skew_feature_extractor.py @@ -1,26 +1,26 @@ -import numpy as np -import scipy.stats.stats as stat - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class SkewZFeatureExtractor(FeatureExtractor): - """Calculates the skew on the z axis.""" - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['skew_z'] - - def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): - if neighborhood: - z = sourcepc[point][self.DATA_KEY]['data'][neighborhood] - var_z = stat.skew(z) - else: - var_z = np.NaN - return var_z +import numpy as np +import scipy.stats.stats as stat + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class SkewFeatureExtractor(FeatureExtractor): + """Calculates the skew on the z axis.""" + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + return ['skew_' + self.data_key] + + def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): + if neighborhood: + source_data = sourcepc[point][self.data_key]['data'][neighborhood] + skew = stat.skew(source_data) + else: + skew = np.NaN + return skew diff --git a/laserchicken/feature_extractor/skew_norm_z_feature_extractor.py b/laserchicken/feature_extractor/skew_norm_z_feature_extractor.py deleted file mode 100644 index 2a5fbd5..0000000 --- a/laserchicken/feature_extractor/skew_norm_z_feature_extractor.py +++ /dev/null @@ -1,11 +0,0 @@ -from laserchicken import keys -from laserchicken.feature_extractor.test_skew_z_feature_extractor import SkewZFeatureExtractor - - -class SkewNormZFeatureExtractor(SkewZFeatureExtractor): - """Calculates the skew on the normalized height.""" - DATA_KEY = keys.normalized_height - - @classmethod - def provides(cls): - return ['skew_norm_z'] diff --git a/laserchicken/feature_extractor/test_all_features.py b/laserchicken/feature_extractor/test_all_features.py index 332d1cf..15c82e5 100644 --- a/laserchicken/feature_extractor/test_all_features.py +++ b/laserchicken/feature_extractor/test_all_features.py @@ -139,18 +139,23 @@ def test_inputNotChanged(feature): assert json.dumps(original_neighborhoods) == json.dumps(neighborhoods) -def _create_point_cloud(x=None, y=None, z=None, norm_z=None, n=10): +def _create_point_cloud(x=None, y=None, z=None, norm_z=None, intensity=None, n=10): tag = GROUND_TAGS[0] - pc = {point: {'x': {'data': np.array([x if x is not None else i for i in range(n)]), 'type': 'float'}, - 'y': {'data': np.array([y if y is not None else i for i in range(n)]), 'type': 'float'}, - 'z': {'data': np.array([z if z is not None else i for i in range(n)]), 'type': 'float'}, - normalized_height: {'data': np.array([norm_z if norm_z is not None else i for i in range(n)]), - 'type': 'float'}, + pc = {point: {'x': _create_attribute(n, fill_value=x), + 'y': _create_attribute(n,fill_value=y), + 'z': _create_attribute(n,fill_value=z), + keys.normalized_height: _create_attribute(n,fill_value=norm_z), + keys.intensity: _create_attribute(n,fill_value=intensity), 'raw_classification': {'data': np.array([i if i % 2 == 0 else tag for i in range(n)]), 'type': 'float'}}} return pc +def _create_attribute(n_points, fill_value=None): + attribute_data = np.array([fill_value if fill_value is not None else i for i in range(n_points)]) + return {'data': attribute_data, 'type': 'float'} + + def _assert_attributes_not_changed(original_point_cloud, new_point_cloud): for attribute in original_point_cloud[keys.point]: np.testing.assert_array_almost_equal(new_point_cloud[keys.point][attribute]['data'], diff --git a/laserchicken/feature_extractor/test_density_absolute_mean_norm_z_extractor.py b/laserchicken/feature_extractor/test_density_absolute_mean_norm_z_extractor.py deleted file mode 100644 index 3be8fdb..0000000 --- a/laserchicken/feature_extractor/test_density_absolute_mean_norm_z_extractor.py +++ /dev/null @@ -1,32 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.density_absolute_mean_norm_z_feature_extractor import \ - DensityAbsoluteMeanNormZFeatureExtractor -from laserchicken.feature_extractor.density_absolute_mean_z_feature_extractor import \ - DensityAbsoluteMeanZFeatureExtractor -from laserchicken.feature_extractor.pulse_penetration_feature_extractor import PulsePenetrationFeatureExtractor -from laserchicken.keys import point -from laserchicken.test_tools import create_point_cloud - - -class TestDensityAbsoluteMeanNormZFeatureExtractorArtificialData(unittest.TestCase): - def test_simle_case_correct(self): - """Check that one out of 4 points above mean of only vegetation points yields a value of 25""" - ground = 2 # Ground tag - veg = 4 # Medium vegetation tag - x = y = z = np.array([10, 10, 10, 1, 1, 1, 2]) - point_cloud = create_point_cloud(x, y, np.zeros_like(z), normalized_z=z) - point_cloud[point]['raw_classification'] = {'data': np.array([ground, ground, ground, veg, veg, veg, veg]), - 'type': 'double'} - neighborhood = list(range(len(x))) - - extractor = DensityAbsoluteMeanNormZFeatureExtractor() - density_absolute_mean = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertAlmostEqual(density_absolute_mean, 25) - - -if __name__ == '__main__': - unittest.main() diff --git a/laserchicken/feature_extractor/test_density_absolute_mean_z_extractor.py b/laserchicken/feature_extractor/test_density_absolute_mean_z_extractor.py index 8a3d3c3..8f03965 100644 --- a/laserchicken/feature_extractor/test_density_absolute_mean_z_extractor.py +++ b/laserchicken/feature_extractor/test_density_absolute_mean_z_extractor.py @@ -2,8 +2,8 @@ import numpy as np -from laserchicken.feature_extractor.density_absolute_mean_z_feature_extractor import \ - DensityAbsoluteMeanZFeatureExtractor +from laserchicken import keys +from laserchicken.feature_extractor.density_absolute_mean_feature_extractor import DensityAbsoluteMeanFeatureExtractor from laserchicken.keys import point from laserchicken.test_tools import create_point_cloud @@ -19,11 +19,36 @@ def test_simle_case_correct(self): 'type': 'double'} neighborhood = list(range(len(x))) - extractor = DensityAbsoluteMeanZFeatureExtractor() - density_absolute_mean = extractor.extract(point_cloud, neighborhood, None, None, None) + density_absolute_mean = self.extractor.extract(point_cloud, neighborhood, None, None, None) self.assertAlmostEqual(density_absolute_mean, 25) + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('density_absolute_mean_z', feature_names) -if __name__ == '__main__': - unittest.main() + def setUp(self): + self.extractor = DensityAbsoluteMeanFeatureExtractor() + + +class TestDensityAbsoluteMeanNormZFeatureExtractorArtificialData(unittest.TestCase): + def test_simle_case_correct(self): + """Check that one out of 4 points above mean of only vegetation points yields a value of 25""" + ground = 2 # Ground tag + veg = 4 # Medium vegetation tag + x = y = z = np.array([10, 10, 10, 1, 1, 1, 2]) + point_cloud = create_point_cloud(x, y, np.zeros_like(z), normalized_z=z) + point_cloud[point]['raw_classification'] = {'data': np.array([ground, ground, ground, veg, veg, veg, veg]), + 'type': 'double'} + neighborhood = list(range(len(x))) + + density_absolute_mean = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertAlmostEqual(density_absolute_mean, 25) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('density_absolute_mean_normalized_height', feature_names) + + def setUp(self): + self.extractor = DensityAbsoluteMeanFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_entropy_z_feature_extractor.py b/laserchicken/feature_extractor/test_entropy_feature_extractor.py similarity index 62% rename from laserchicken/feature_extractor/test_entropy_z_feature_extractor.py rename to laserchicken/feature_extractor/test_entropy_feature_extractor.py index c06c297..4a54dc6 100644 --- a/laserchicken/feature_extractor/test_entropy_z_feature_extractor.py +++ b/laserchicken/feature_extractor/test_entropy_feature_extractor.py @@ -2,7 +2,11 @@ import random import unittest +import numpy as np + from laserchicken import compute_neighbors, feature_extractor, keys, read_las, utils +from laserchicken.feature_extractor.entropy_feature_extractor import EntropyFeatureExtractor +from laserchicken.test_tools import create_point_cloud from laserchicken.volume_specification import InfiniteCylinder @@ -32,15 +36,36 @@ def test_entropy_in_cylinders(self): for i in range(n_targets): H = utils.get_attribute_value(target_point_cloud, i, "entropy_z") self.assertTrue(H >= 0) - self.assertEqual("laserchicken.feature_extractor.entropy_z_feature_extractor", + self.assertEqual("laserchicken.feature_extractor.entropy_feature_extractor", target_point_cloud[keys.provenance][0]["module"]) self.assertEqual( [0.1], target_point_cloud[keys.provenance][0]["parameters"]) + def test_default_provides_correct(self): + feature_names = EntropyFeatureExtractor().provides() + self.assertIn('entropy_z', feature_names) + def setUp(self): self.point_cloud = read_las.read(os.path.join( self._test_data_source, self._test_file_name)) random.seed(102938482634) - def tearDown(self): - pass + +class TestExtractNormalizedEntropy(unittest.TestCase): + def test_use_norm_z(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 5]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + entropy = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertNotAlmostEqual(entropy, 0) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('entropy_normalized_height', feature_names) + + def setUp(self): + self.extractor = EntropyFeatureExtractor(data_key=keys.normalized_height) \ No newline at end of file diff --git a/laserchicken/feature_extractor/test_entropy_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_entropy_norm_z_feature_extractor.py deleted file mode 100644 index 0b7d4e0..0000000 --- a/laserchicken/feature_extractor/test_entropy_norm_z_feature_extractor.py +++ /dev/null @@ -1,23 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.entropy_norm_z_feature_extractor import EntropyNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestExtractEntropy(unittest.TestCase): - def test_use_norm_z(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 5]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = EntropyNormZFeatureExtractor() - entropy = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertNotAlmostEqual(entropy, 0) - - - diff --git a/laserchicken/feature_extractor/test_kurtosis_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_kurtosis_norm_z_feature_extractor.py deleted file mode 100644 index 6334434..0000000 --- a/laserchicken/feature_extractor/test_kurtosis_norm_z_feature_extractor.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.kurtosis_norm_z_feature_extractor import KurtosisNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestKurtosisNormZFeatureExtractor(unittest.TestCase): - def test_use_norm_z(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 5]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = KurtosisNormZFeatureExtractor() - kurtosis = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertAlmostEqual(kurtosis, -1.5) diff --git a/laserchicken/feature_extractor/test_kurtosis_z_feature_extractor.py b/laserchicken/feature_extractor/test_kurtosis_z_feature_extractor.py index 3f3727c..1b10aa8 100644 --- a/laserchicken/feature_extractor/test_kurtosis_z_feature_extractor.py +++ b/laserchicken/feature_extractor/test_kurtosis_z_feature_extractor.py @@ -2,8 +2,9 @@ import numpy as np -from laserchicken import read_las -from laserchicken.feature_extractor.kurtosis_z_feature_extractor import KurtosisZFeatureExtractor +from laserchicken import read_las, keys +from laserchicken.feature_extractor.kurtosis_feature_extractor import KurtosisFeatureExtractor +from laserchicken.test_tools import create_point_cloud class TestKurtosisZFeatureExtractor(unittest.TestCase): @@ -12,11 +13,38 @@ def test_height_stats(self): neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, 7018, 61494, 65194, 117931, 62971, 10474, 90322] - kurtosis_z = KurtosisZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) + kurtosis_z = self.extractor.extract(pc_in, neighborhood, None, None, None) np.testing.assert_allclose(kurtosis_z, 3.968414258629714) def test_height_stats_without_neighbors(self): pc_in = read_las.read("testdata/AHN2.las") neighborhood = [] - kurtosis_z = KurtosisZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) + kurtosis_z = self.extractor.extract(pc_in, neighborhood, pc_in, None, None) assert np.isnan(kurtosis_z) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('kurto_z', feature_names) + + def setUp(self): + self.extractor = KurtosisFeatureExtractor() + + +class TestKurtosisNormZFeatureExtractor(unittest.TestCase): + def test_use_norm_z(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 5]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + kurtosis = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertAlmostEqual(kurtosis, -1.5) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('kurto_normalized_height', feature_names) + + def setUp(self): + self.extractor = KurtosisFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_mean_std_coeff_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_mean_std_coeff_norm_z_feature_extractor.py deleted file mode 100644 index c0329e4..0000000 --- a/laserchicken/feature_extractor/test_mean_std_coeff_norm_z_feature_extractor.py +++ /dev/null @@ -1,22 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.mean_std_coeff_norm_z_feature_extractor import MeanStdCoeffNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestMeanStdCoeffNormZFeatureExtractor(unittest.TestCase): - def test_height_stats(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 5]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = MeanStdCoeffNormZFeatureExtractor() - mean, std, coeff = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertAlmostEquals(mean, 4) - self.assertAlmostEquals(std, np.sqrt(2/3)) - self.assertAlmostEquals(coeff, np.sqrt(2/3)/4) diff --git a/laserchicken/feature_extractor/test_mean_std_coeff_z_feature_extractor.py b/laserchicken/feature_extractor/test_mean_std_coeff_z_feature_extractor.py index 0d4a9ab..06069ce 100644 --- a/laserchicken/feature_extractor/test_mean_std_coeff_z_feature_extractor.py +++ b/laserchicken/feature_extractor/test_mean_std_coeff_z_feature_extractor.py @@ -2,8 +2,9 @@ import numpy as np -from laserchicken import read_las -from laserchicken.feature_extractor.mean_std_coeff_z_feature_extractor import MeanStdCoeffZFeatureExtractor +from laserchicken import read_las, keys +from laserchicken.feature_extractor.mean_std_coeff_feature_extractor import MeanStdCoeffFeatureExtractor +from laserchicken.test_tools import create_point_cloud class TestMeanZFeatureExtractor(unittest.TestCase): @@ -12,7 +13,7 @@ def test_height_stats(self): neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, 7018, 61494, 65194, 117931, 62971, 10474, 90322] - mean_z, std_z, coeff_var_z = MeanStdCoeffZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) + mean_z, std_z, coeff_var_z = self.extractor.extract(pc_in, neighborhood, None, None, None) np.testing.assert_allclose(mean_z, 1.3779999737739566) np.testing.assert_allclose(std_z, 1.3567741153191268) np.testing.assert_allclose(coeff_var_z, 0.9845966191155302) @@ -20,7 +21,40 @@ def test_height_stats(self): def test_height_stats_without_neighbors(self): pc_in = read_las.read("testdata/AHN2.las") neighborhood = [] - mean_z, std_z, coeff_var_z = MeanStdCoeffZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) + mean_z, std_z, coeff_var_z = self.extractor.extract(pc_in, neighborhood, pc_in, None, None) assert np.isnan(mean_z) assert np.isnan(std_z) assert np.isnan(coeff_var_z) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('mean_z', feature_names) + self.assertIn('std_z', feature_names) + self.assertIn('coeff_var_z', feature_names) + + def setUp(self): + self.extractor = MeanStdCoeffFeatureExtractor() + + +class TestMeanStdCoeffNormZFeatureExtractor(unittest.TestCase): + def test_height_stats(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 5]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + mean, std, coeff = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertAlmostEquals(mean, 4) + self.assertAlmostEquals(std, np.sqrt(2 / 3)) + self.assertAlmostEquals(coeff, np.sqrt(2 / 3) / 4) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('mean_normalized_height', feature_names) + self.assertIn('std_normalized_height', feature_names) + self.assertIn('coeff_var_normalized_height', feature_names) + + def setUp(self): + self.extractor = MeanStdCoeffFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_median_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_median_norm_z_feature_extractor.py deleted file mode 100644 index 8e096dc..0000000 --- a/laserchicken/feature_extractor/test_median_norm_z_feature_extractor.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.median_norm_z_feature_extractor import MedianNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestMedianNormZFeatureExtractor(unittest.TestCase): - def test_use_norm_z(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 6]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = MedianNormZFeatureExtractor() - median = extractor.extract(point_cloud, neighborhood, None, None, None) - - np.testing.assert_almost_equal(median, 4) diff --git a/laserchicken/feature_extractor/test_median_z_feature_extractor.py b/laserchicken/feature_extractor/test_median_z_feature_extractor.py index cba6f3d..19d0471 100644 --- a/laserchicken/feature_extractor/test_median_z_feature_extractor.py +++ b/laserchicken/feature_extractor/test_median_z_feature_extractor.py @@ -2,8 +2,9 @@ import numpy as np -from laserchicken import read_las -from laserchicken.feature_extractor.median_z_feature_extractor import MedianZFeatureExtractor +from laserchicken import read_las, keys +from laserchicken.feature_extractor.median_feature_extractor import MedianFeatureExtractor +from laserchicken.test_tools import create_point_cloud class TestMedianZFeatureExtractor(unittest.TestCase): @@ -12,11 +13,38 @@ def test_height_stats(self): neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, 7018, 61494, 65194, 117931, 62971, 10474, 90322] - median_z = MedianZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) + median_z = self.extractor.extract(pc_in, neighborhood, None, None, None) np.testing.assert_allclose(median_z, 0.69999997377395629) def test_height_stats_without_neighbors(self): pc_in = read_las.read("testdata/AHN2.las") neighborhood = [] - median_z = MedianZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) + median_z = self.extractor.extract(pc_in, neighborhood, pc_in, None, None) assert np.isnan(median_z) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('median_z', feature_names) + + def setUp(self): + self.extractor = MedianFeatureExtractor() + + +class TestMedianNormZFeatureExtractor(unittest.TestCase): + def test_use_norm_z(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 6]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + median = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + np.testing.assert_almost_equal(median, 4) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('median_normalized_height', feature_names) + + def setUp(self): + self.extractor = MedianFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_percentile_norm_z.py b/laserchicken/feature_extractor/test_percentile_norm_z.py deleted file mode 100644 index 96f0aa0..0000000 --- a/laserchicken/feature_extractor/test_percentile_norm_z.py +++ /dev/null @@ -1,24 +0,0 @@ -import itertools -import unittest - -import numpy as np - -from laserchicken.feature_extractor.percentile_norm_z_feature_extractor import PercentileNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestPercentileNormZFeatureExtractorArtificialData(unittest.TestCase): - """Test percentile feature extractor on artificial data.""" - - def test_percentile_norm_z(self): - xyz = np.array([list(p) for p in list(itertools.product(np.linspace(0, 1, 11), repeat=3))]) - point_cloud = create_point_cloud(xyz[:, 0], xyz[:, 1], np.zeros_like(xyz[:, 2]), normalized_z=xyz[:, 2]) - expected = np.linspace(0.1, 1.0, 10) - extractors = [PercentileNormZFeatureExtractor(p) for p in range(10, 110, 10)] - - percentiles = np.hstack([e.extract(point_cloud, range(len(xyz)), None, None, None) for e in extractors]) - - np.testing.assert_allclose(percentiles, expected) - -if __name__ == '__main__': - unittest.main() diff --git a/laserchicken/feature_extractor/test_percentile_z.py b/laserchicken/feature_extractor/test_percentile_z.py index 8a3e6f9..d4cca75 100644 --- a/laserchicken/feature_extractor/test_percentile_z.py +++ b/laserchicken/feature_extractor/test_percentile_z.py @@ -1,10 +1,11 @@ +import itertools import os -import random import unittest -import itertools + import numpy as np + from laserchicken import read_las, keys -from laserchicken.feature_extractor.percentile_z_feature_extractor import PercentileZFeatureExtractor +from laserchicken.feature_extractor.percentile_feature_extractor import PercentileFeatureExtractor from laserchicken.test_tools import create_point_cloud @@ -15,12 +16,16 @@ def test_percentile_z(self): xyz = np.array([list(p) for p in list(itertools.product(np.linspace(0, 1, 11), repeat=3))]) point_cloud = create_point_cloud(xyz[:, 0], xyz[:, 1], xyz[:, 2]) expected = np.linspace(0.1, 1.0, 10) - extractors = [PercentileZFeatureExtractor(p) for p in range(10, 110, 10)] + extractors = [PercentileFeatureExtractor(p) for p in range(10, 110, 10)] percentiles = [e.extract(point_cloud, range(len(xyz)), None, None, None) for e in extractors] np.testing.assert_allclose(percentiles, expected) + def test_default_provides_correct(self): + feature_names = PercentileFeatureExtractor(54).provides() + self.assertIn('perc_54_z', feature_names) + class TestPercentileFeatureExtractorRealData(unittest.TestCase): """Test percentile feature extractor on real data.""" @@ -35,10 +40,24 @@ def test_percentile(self): 89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, 7018, 61494, 65194, 117931, 62971, 10474, 90322 ] - extractor = PercentileZFeatureExtractor() + extractor = PercentileFeatureExtractor() extractor.extract(point_cloud, index, None, None, None) -if __name__ == '__main__': - unittest.main() +class TestPercentileNormZFeatureExtractorArtificialData(unittest.TestCase): + """Test percentile feature extractor on artificial data.""" + + def test_percentile_norm_z(self): + xyz = np.array([list(p) for p in list(itertools.product(np.linspace(0, 1, 11), repeat=3))]) + point_cloud = create_point_cloud(xyz[:, 0], xyz[:, 1], np.zeros_like(xyz[:, 2]), normalized_z=xyz[:, 2]) + expected = np.linspace(0.1, 1.0, 10) + extractors = [PercentileFeatureExtractor(p, data_key=keys.normalized_height) for p in range(10, 110, 10)] + + percentiles = np.hstack([e.extract(point_cloud, range(len(xyz)), None, None, None) for e in extractors]) + + np.testing.assert_allclose(percentiles, expected) + + def test_default_provides_correct(self): + feature_names = PercentileFeatureExtractor(54, data_key=keys.normalized_height).provides() + self.assertIn('perc_54_normalized_height', feature_names) diff --git a/laserchicken/feature_extractor/test_range_feature_extractor.py b/laserchicken/feature_extractor/test_range_feature_extractor.py new file mode 100644 index 0000000..153f109 --- /dev/null +++ b/laserchicken/feature_extractor/test_range_feature_extractor.py @@ -0,0 +1,55 @@ +import unittest + +import numpy as np + +from laserchicken import read_las, keys +from laserchicken.feature_extractor.range_feature_extractor import RangeFeatureExtractor +from laserchicken.test_tools import create_point_cloud + + +class TestRangeZFeatureExtractor(unittest.TestCase): + def test_height_stats(self): + pc_in = read_las.read("testdata/AHN2.las") + neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, + 7018, + 61494, 65194, 117931, 62971, 10474, 90322] + max_z, min_z, range_z = RangeFeatureExtractor().extract(pc_in, neighborhood, None, None, None) + np.testing.assert_allclose(range_z, 5.5) + np.testing.assert_allclose(max_z, 5.979999973773956) + np.testing.assert_allclose(min_z, 0.47999997377395631) + + def test_height_stats_without_neighbors(self): + pc_in = read_las.read("testdata/AHN2.las") + neighborhood = [] + max_z, min_z, range_z = RangeFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) + assert np.isnan(range_z) + assert np.isnan(max_z) + assert np.isnan(min_z) + + def test_default_provides_correct(self): + feature_names = RangeFeatureExtractor().provides() + self.assertIn('min_z', feature_names) + self.assertIn('max_z', feature_names) + self.assertIn('range_z', feature_names) + + +class TestRangeNormZFeatureExtractor(unittest.TestCase): + def test_use_norm(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 5]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + extractor = RangeFeatureExtractor(data_key=keys.normalized_height) + _max, _min, _range = extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertAlmostEquals(_max, 5) + self.assertAlmostEquals(_min, 3) + self.assertAlmostEquals(_range, 2) + + def test_normalized_z_provides_correct(self): + feature_names = RangeFeatureExtractor(data_key=keys.normalized_height).provides() + self.assertIn('min_normalized_height', feature_names) + self.assertIn('max_normalized_height', feature_names) + self.assertIn('range_normalized_height', feature_names) \ No newline at end of file diff --git a/laserchicken/feature_extractor/test_range_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_range_norm_z_feature_extractor.py deleted file mode 100644 index 8d990c8..0000000 --- a/laserchicken/feature_extractor/test_range_norm_z_feature_extractor.py +++ /dev/null @@ -1,22 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.range_norm_z_feature_extractor import RangeNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestRangeNormZFeatureExtractor(unittest.TestCase): - def test_use_norm(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 5]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = RangeNormZFeatureExtractor() - _max, _min, _range = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertAlmostEquals(_max, 5) - self.assertAlmostEquals(_min, 3) - self.assertAlmostEquals(_range, 2) diff --git a/laserchicken/feature_extractor/test_range_z_feature_extractor.py b/laserchicken/feature_extractor/test_range_z_feature_extractor.py deleted file mode 100644 index 4d30c83..0000000 --- a/laserchicken/feature_extractor/test_range_z_feature_extractor.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken import read_las -from laserchicken.feature_extractor.range_z_feature_extractor import RangeZFeatureExtractor - - -class TestRangeZFeatureExtractor(unittest.TestCase): - def test_height_stats(self): - pc_in = read_las.read("testdata/AHN2.las") - neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, - 7018, - 61494, 65194, 117931, 62971, 10474, 90322] - max_z, min_z, range_z = RangeZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) - np.testing.assert_allclose(range_z, 5.5) - np.testing.assert_allclose(max_z, 5.979999973773956) - np.testing.assert_allclose(min_z, 0.47999997377395631) - - def test_height_stats_without_neighbors(self): - pc_in = read_las.read("testdata/AHN2.las") - neighborhood = [] - max_z, min_z, range_z = RangeZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) - assert np.isnan(range_z) - assert np.isnan(max_z) - assert np.isnan(min_z) diff --git a/laserchicken/feature_extractor/test_skew_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_skew_norm_z_feature_extractor.py deleted file mode 100644 index 1937a4c..0000000 --- a/laserchicken/feature_extractor/test_skew_norm_z_feature_extractor.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.skew_norm_z_feature_extractor import SkewNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestSkewZFeatureExtractor(unittest.TestCase): - def test_use_norm_z(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 6]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = SkewNormZFeatureExtractor() - skew = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertGreater(skew, 0.1) diff --git a/laserchicken/feature_extractor/test_skew_z_feature_extractor.py b/laserchicken/feature_extractor/test_skew_z_feature_extractor.py index 47efa4b..1652685 100644 --- a/laserchicken/feature_extractor/test_skew_z_feature_extractor.py +++ b/laserchicken/feature_extractor/test_skew_z_feature_extractor.py @@ -2,8 +2,9 @@ import numpy as np -from laserchicken import read_las -from laserchicken.feature_extractor.skew_z_feature_extractor import SkewZFeatureExtractor +from laserchicken import read_las, keys +from laserchicken.feature_extractor.skew_feature_extractor import SkewFeatureExtractor +from laserchicken.test_tools import create_point_cloud class TestSkewZFeatureExtractor(unittest.TestCase): @@ -12,11 +13,38 @@ def test_height_stats(self): neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, 7018, 61494, 65194, 117931, 62971, 10474, 90322] - skew_z = SkewZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) + skew_z = self.extractor.extract(pc_in, neighborhood, None, None, None) np.testing.assert_allclose(skew_z, 2.083098281031817) def test_height_stats_without_neighbors(self): pc_in = read_las.read("testdata/AHN2.las") neighborhood = [] - skew_z = SkewZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) + skew_z = self.extractor.extract(pc_in, neighborhood, pc_in, None, None) assert np.isnan(skew_z) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('skew_z', feature_names) + + def setUp(self): + self.extractor = SkewFeatureExtractor() + + +class TestSkewZFeatureExtractor(unittest.TestCase): + def test_use_norm_z(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 6]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + skew = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertGreater(skew, 0.1) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('skew_normalized_height', feature_names) + + def setUp(self): + self.extractor = SkewFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_var_feature_extractor.py b/laserchicken/feature_extractor/test_var_feature_extractor.py new file mode 100644 index 0000000..4975eee --- /dev/null +++ b/laserchicken/feature_extractor/test_var_feature_extractor.py @@ -0,0 +1,50 @@ +import unittest + +import numpy as np + +from laserchicken import read_las, keys +from laserchicken.feature_extractor.var_feature_extractor import VarianceFeatureExtractor +from laserchicken.test_tools import create_point_cloud + + +class TestVariationZFeatureExtractor(unittest.TestCase): + def test_height_stats(self): + pc_in = read_las.read("testdata/AHN2.las") + neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, + 7018, + 61494, 65194, 117931, 62971, 10474, 90322] + var_z = self.extractor.extract(pc_in, neighborhood, None, None, None) + np.testing.assert_allclose(var_z, 1.8408359999999995) + + def test_height_stats_without_neighbors(self): + pc_in = read_las.read("testdata/AHN2.las") + neighborhood = [] + var_z = self.extractor.extract(pc_in, neighborhood, pc_in, None, None) + assert np.isnan(var_z) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('var_z', feature_names) + + def setUp(self): + self.extractor = VarianceFeatureExtractor() + + +class TestVarianceNormZFeatureExtractor(unittest.TestCase): + def test_height_stats(self): + x = y = np.array([0, 0, 0]) + z = np.array([2, 2, 2]) + normalized_z = np.array([3, 4, 5]) + point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) + neighborhood = [[0, 1, 2]] + + variance = self.extractor.extract(point_cloud, neighborhood, None, None, None) + + self.assertAlmostEquals(variance, 2 / 3) + + def test_default_provides_correct(self): + feature_names = self.extractor.provides() + self.assertIn('var_normalized_height', feature_names) + + def setUp(self): + self.extractor = VarianceFeatureExtractor(data_key=keys.normalized_height) diff --git a/laserchicken/feature_extractor/test_var_norm_z_feature_extractor.py b/laserchicken/feature_extractor/test_var_norm_z_feature_extractor.py deleted file mode 100644 index ae24040..0000000 --- a/laserchicken/feature_extractor/test_var_norm_z_feature_extractor.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken.feature_extractor.var_norm_z_feature_extractor import VarianceNormZFeatureExtractor -from laserchicken.test_tools import create_point_cloud - - -class TestVarianceNormZFeatureExtractor(unittest.TestCase): - def test_height_stats(self): - x = y = np.array([0, 0, 0]) - z = np.array([2, 2, 2]) - normalized_z = np.array([3, 4, 5]) - point_cloud = create_point_cloud(x, y, z, normalized_z=normalized_z) - neighborhood = [[0, 1, 2]] - - extractor = VarianceNormZFeatureExtractor() - variance = extractor.extract(point_cloud, neighborhood, None, None, None) - - self.assertAlmostEquals(variance, 2/3) diff --git a/laserchicken/feature_extractor/test_var_z_feature_extractor.py b/laserchicken/feature_extractor/test_var_z_feature_extractor.py deleted file mode 100644 index 4c71bed..0000000 --- a/laserchicken/feature_extractor/test_var_z_feature_extractor.py +++ /dev/null @@ -1,22 +0,0 @@ -import unittest - -import numpy as np - -from laserchicken import read_las -from laserchicken.feature_extractor.var_z_feature_extractor import VarianceZFeatureExtractor - - -class TestVariationZFeatureExtractor(unittest.TestCase): - def test_height_stats(self): - pc_in = read_las.read("testdata/AHN2.las") - neighborhood = [89664, 23893, 30638, 128795, 62052, 174453, 29129, 17127, 128215, 29667, 116156, 119157, 98591, - 7018, - 61494, 65194, 117931, 62971, 10474, 90322] - var_z = VarianceZFeatureExtractor().extract(pc_in, neighborhood, None, None, None) - np.testing.assert_allclose(var_z, 1.8408359999999995) - - def test_height_stats_without_neighbors(self): - pc_in = read_las.read("testdata/AHN2.las") - neighborhood = [] - var_z = VarianceZFeatureExtractor().extract(pc_in, neighborhood, pc_in, None, None) - assert np.isnan(var_z) diff --git a/laserchicken/feature_extractor/var_feature_extractor.py b/laserchicken/feature_extractor/var_feature_extractor.py new file mode 100644 index 0000000..ce7fec0 --- /dev/null +++ b/laserchicken/feature_extractor/var_feature_extractor.py @@ -0,0 +1,25 @@ +import numpy as np + +from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor +from laserchicken.keys import point + + +class VarianceFeatureExtractor(FeatureExtractor): + """Calculates the variation on the z axis.""" + def __init__(self, data_key='z'): + self.data_key = data_key + + @classmethod + def requires(cls): + return [] + + def provides(self): + return ['var_' + self.data_key] + + def extract(self, source_point_cloud, neighborhood, targetpc, target_index, volume_description): + if neighborhood: + source_data = source_point_cloud[point][self.data_key]['data'][neighborhood] + var_z = np.var(source_data) + else: + var_z = np.NaN + return var_z diff --git a/laserchicken/feature_extractor/var_norm_z_feature_extractor.py b/laserchicken/feature_extractor/var_norm_z_feature_extractor.py deleted file mode 100644 index 71559f4..0000000 --- a/laserchicken/feature_extractor/var_norm_z_feature_extractor.py +++ /dev/null @@ -1,12 +0,0 @@ -from laserchicken import keys -from laserchicken.feature_extractor.var_z_feature_extractor import VarianceZFeatureExtractor - - -class VarianceNormZFeatureExtractor(VarianceZFeatureExtractor): - """Calculates the variation on the z axis.""" - DATA_KEY = keys.normalized_height - - @classmethod - def provides(cls): - return ['var_norm_z'] - diff --git a/laserchicken/feature_extractor/var_z_feature_extractor.py b/laserchicken/feature_extractor/var_z_feature_extractor.py deleted file mode 100644 index b50da70..0000000 --- a/laserchicken/feature_extractor/var_z_feature_extractor.py +++ /dev/null @@ -1,25 +0,0 @@ -import numpy as np - -from laserchicken.feature_extractor.base_feature_extractor import FeatureExtractor -from laserchicken.keys import point - - -class VarianceZFeatureExtractor(FeatureExtractor): - """Calculates the variation on the z axis.""" - DATA_KEY = 'z' - - @classmethod - def requires(cls): - return [] - - @classmethod - def provides(cls): - return ['var_z'] - - def extract(self, sourcepc, neighborhood, targetpc, targetindex, volume_description): - if neighborhood: - z = sourcepc[point][self.DATA_KEY]['data'][neighborhood] - var_z = np.var(z) - else: - var_z = np.NaN - return var_z diff --git a/laserchicken/keys.py b/laserchicken/keys.py index 3c2f9f7..6d465c4 100644 --- a/laserchicken/keys.py +++ b/laserchicken/keys.py @@ -4,6 +4,9 @@ # Name of the normalized height point attribute normalized_height = 'normalized_height' +# Name of the intensity point attribute +intensity = 'intensity' + # point_cloud = 'pointcloud' diff --git a/laserchicken/normalization.py b/laserchicken/normalization.py index 2b0f72a..ba27fd5 100644 --- a/laserchicken/normalization.py +++ b/laserchicken/normalization.py @@ -1,6 +1,6 @@ from laserchicken.compute_neighbors import compute_neighborhoods from laserchicken import keys -from laserchicken.feature_extractor.range_z_feature_extractor import RangeZFeatureExtractor as range_extractor +from laserchicken.feature_extractor.range_feature_extractor import RangeFeatureExtractor as range_extractor from laserchicken.keys import normalized_height import numpy as np diff --git a/laserchicken/test_feature_extractor/test_feature_map.py b/laserchicken/test_feature_extractor/test_feature_map.py index b7dd378..2bbca29 100644 --- a/laserchicken/test_feature_extractor/test_feature_map.py +++ b/laserchicken/test_feature_extractor/test_feature_map.py @@ -11,11 +11,12 @@ def test__feature_map(self): expected_features = ['point_density', 'echo_ratio', 'eigenv_1', 'eigenv_2', 'eigenv_3', 'normal_vector_1', 'normal_vector_2', 'normal_vector_3', 'slope', 'entropy_z', 'pulse_penetration_ratio', 'sigma_z', 'median_z', 'max_z', 'min_z', - 'range_z', 'var_z', 'mean_z', 'std_z', 'coeff_var_z', 'skew_z', 'kurto_z', 'skew_norm_z', - 'mean_norm_z', 'std_norm_z', 'coeff_var_norm_z', 'var_norm_z', 'max_norm_z', 'min_norm_z', - 'range_norm_z', 'kurto_norm_z', 'entropy_norm_z', 'median_norm_z', - 'density_absolute_mean_z', 'density_absolute_mean_norm_z', 'perc_15_z', - 'perc_99_normalized_height'] + 'range_z', 'var_z', 'mean_z', 'std_z', 'coeff_var_z', 'skew_z', 'kurto_z', 'skew_normalized_height', + 'mean_normalized_height','std_normalized_height', 'coeff_var_normalized_height', 'var_normalized_height', 'min_normalized_height', + 'max_normalized_height', 'range_normalized_height', 'kurto_normalized_height', 'entropy_normalized_height', + 'median_normalized_height', 'max_intensity', + 'density_absolute_mean_z', 'density_absolute_mean_normalized_height', 'perc_15_z', + 'perc_99_normalized_height', 'max_intensity', 'min_intensity', 'range_intensity', 'mean_intensity', 'std_intensity', 'coeff_var_intensity'] for feature in expected_features: self.assertIn(feature, feature_extractor.FEATURES)