1010from laserchicken import keys , utils
1111from .density_feature_extractor import PointDensityFeatureExtractor
1212from .echo_ratio_feature_extractor import EchoRatioFeatureExtractor
13- from .eigenvals_feature_extractor import EigenValueFeatureExtractor
14- from .entropy_feature_extractor import EntropyFeatureExtractor
15- from .height_statistics_feature_extractor import HeightStatisticsFeatureExtractor
16- from .normal_plane_feature_extractor import NormalPlaneFeatureExtractor
17- from .percentile_feature_extractor import PercentileFeatureExtractor
13+ from .eigenvals_feature_extractor import EigenValueVectorizeFeatureExtractor
14+ from .entropy_z_feature_extractor import EntropyZFeatureExtractor
15+ from .percentile_z_feature_extractor import PercentileZFeatureExtractor
1816from .pulse_penetration_feature_extractor import PulsePenetrationFeatureExtractor
1917from .sigma_z_feature_extractor import SigmaZFeatureExtractor
18+ from .median_z_feature_extractor import MedianZFeatureExtractor
19+ from .range_z_feature_extractor import RangeZFeatureExtractor
20+ from .var_z_feature_extractor import VarianceZFeatureExtractor
21+ from .mean_std_coeff_z_feature_extractor import MeanStdCoeffZFeatureExtractor
22+ from .skew_z_feature_extractor import SkewZFeatureExtractor
23+ from .kurtosis_z_feature_extractor import KurtosisZFeatureExtractor
24+ from .skew_norm_z_feature_extractor import SkewNormZFeatureExtractor
25+ from .mean_std_coeff_norm_z_feature_extractor import MeanStdCoeffNormZFeatureExtractor
26+ from .var_norm_z_feature_extractor import VarianceNormZFeatureExtractor
27+ from .range_norm_z_feature_extractor import RangeNormZFeatureExtractor
28+ from .kurtosis_norm_z_feature_extractor import KurtosisNormZFeatureExtractor
29+ from .entropy_norm_z_feature_extractor import EntropyNormZFeatureExtractor
30+ from .median_norm_z_feature_extractor import MedianNormZFeatureExtractor
31+ from .percentile_norm_z_feature_extractor import PercentileNormZFeatureExtractor
32+ from .density_absolute_mean_z_feature_extractor import DensityAbsoluteMeanZFeatureExtractor
33+ from .density_absolute_mean_norm_z_feature_extractor import DensityAbsoluteMeanNormZFeatureExtractor
34+
35+
36+ def _create_feature_map (module_name = __name__ ):
37+ """Construct a mapping from feature names to feature extractor classes."""
38+ name_extractor_pairs = _find_name_extractor_pairs (module_name )
39+ return {feature_name : extractor for feature_name , extractor in name_extractor_pairs }
2040
2141
22- def _feature_map (module_name = __name__ ):
23- """Construct a mapping from feature names to feature extractor classes."""
42+ def _find_name_extractor_pairs (module_name ):
2443 module = importlib .import_module (module_name )
25- return {feature_name : extractor
26- for name , extractor in vars (module ).items () if re .match ('^[A-Z][a-zA-Z0-9_]*FeatureExtractor$' , name )
27- for feature_name in extractor .provides ()
28- }
44+ name_extractor_pairs = [(feature_name , extractor )
45+ for name , extractor in vars (module ).items () if
46+ re .match ('^[A-Z][a-zA-Z0-9_]*FeatureExtractor$' , name )
47+ for feature_name in extractor .provides ()]
48+ return name_extractor_pairs
2949
3050
31- FEATURES = _feature_map ()
51+ FEATURES = _create_feature_map ()
3252
3353
34- def compute_features (env_point_cloud , neighborhoods , target_idx_base , target_point_cloud , feature_names , volume , overwrite = False ,
54+ def compute_features (env_point_cloud , neighborhoods , target_idx_base , target_point_cloud , feature_names , volume ,
55+ overwrite = False ,
3556 verbose = True , ** kwargs ):
3657 """
3758 Compute features for each target and store result as point attributes in target point cloud.
@@ -60,18 +81,23 @@ def compute_features(env_point_cloud, neighborhoods, target_idx_base, target_poi
6081 :return: None, results are stored in attributes of the target point cloud
6182 """
6283 _verify_feature_names (feature_names )
63- ordered_features = _make_feature_list (feature_names )
84+ wanted_feature_names = feature_names + [existing_feature for existing_feature in target_point_cloud [keys .point ]]
85+ extended_features = _make_extended_feature_list (feature_names )
86+ features_to_do = extended_features
6487
65- for feature in ordered_features :
66- if (target_idx_base == 0 ) and (not overwrite ) and (feature in target_point_cloud [keys .point ]):
88+ while features_to_do :
89+ feature_name = features_to_do [0 ]
90+
91+ if (target_idx_base == 0 ) and (not overwrite ) and (feature_name in target_point_cloud [keys .point ]):
6792 continue # Skip feature calc if it is already there and we do not overwrite
6893
94+ extractor = FEATURES [feature_name ]()
95+
6996 if verbose :
70- sys .stdout .write ('Feature "{}"\n ' .format (feature ))
97+ sys .stdout .write ('Feature(s) "{}"' .format (extractor . provides () ))
7198 sys .stdout .flush ()
7299 start = time .time ()
73100
74- extractor = FEATURES [feature ]()
75101 _add_or_update_feature (env_point_cloud , neighborhoods , target_idx_base ,
76102 target_point_cloud , extractor , volume , overwrite , kwargs )
77103 utils .add_metadata (target_point_cloud , type (
@@ -82,6 +108,21 @@ def compute_features(env_point_cloud, neighborhoods, target_idx_base, target_poi
82108 sys .stdout .write (' took {:.2f} seconds\n ' .format (elapsed ))
83109 sys .stdout .flush ()
84110
111+ for provided_feature in extractor .provides ():
112+ if provided_feature in features_to_do :
113+ features_to_do .remove (provided_feature )
114+
115+ _keep_only_wanted_features (target_point_cloud , wanted_feature_names )
116+
117+
118+ def _keep_only_wanted_features (target_point_cloud , wanted_feature_names ):
119+ redundant_features = [f for f in target_point_cloud [keys .point ] if f not in wanted_feature_names ]
120+ if redundant_features :
121+ print ('The following unrequested features were calculated as a side effect, but will not be returned:' ,
122+ redundant_features )
123+ for f in redundant_features :
124+ target_point_cloud [keys .point ].pop (f )
125+
85126
86127def _verify_feature_names (feature_names ):
87128 unknown_features = [f for f in feature_names if f not in FEATURES ]
@@ -90,8 +131,8 @@ def _verify_feature_names(feature_names):
90131 .format (', ' .join (unknown_features ), ', ' .join (FEATURES .keys ())))
91132
92133
93- def _add_or_update_feature (env_point_cloud , neighborhoods , target_idx_base , target_point_cloud , extractor , volume , overwrite , kwargs ):
94- #n_targets = len(target_point_cloud[keys.point]["x"]["data"])
134+ def _add_or_update_feature (env_point_cloud , neighborhoods , target_idx_base , target_point_cloud , extractor , volume ,
135+ overwrite , kwargs ):
95136 n_targets = len (neighborhoods )
96137
97138 for k in kwargs :
@@ -101,35 +142,73 @@ def _add_or_update_feature(env_point_cloud, neighborhoods, target_idx_base, targ
101142 feature_values = [np .empty (n_targets , dtype = np .float64 )
102143 for i in range (n_features )]
103144
104- print ("Number of targets: %d, number of features: %d" % (n_targets , n_features ))
145+ if hasattr (extractor , 'is_vectorized' ):
146+ _add_or_update_feature_in_chunks (env_point_cloud , extractor , feature_values , n_features , n_targets ,
147+ neighborhoods ,
148+ target_idx_base , target_point_cloud , volume )
149+ else :
150+ _add_or_update_feature_one_by_one (env_point_cloud , extractor , feature_values , n_features , n_targets ,
151+ neighborhoods , target_idx_base , target_point_cloud , volume )
152+
153+ for i in range (n_features ):
154+ feature = provided_features [i ]
155+ if target_idx_base != 0 :
156+ if feature not in target_point_cloud [keys .point ]:
157+ continue
158+
159+ target_point_cloud [keys .point ][feature ]["data" ] = np .hstack (
160+ [target_point_cloud [keys .point ][feature ]["data" ], feature_values [i ]])
161+
162+ elif overwrite or (feature not in target_point_cloud [keys .point ]):
163+ target_point_cloud [keys .point ][feature ] = {
164+ "type" : 'float64' , "data" : feature_values [i ]}
165+
166+
167+ def _add_or_update_feature_one_by_one (env_point_cloud , extractor , feature_values , n_features , n_targets , neighborhoods ,
168+ target_idx_base , target_point_cloud , volume ):
105169 for target_index in range (n_targets ):
106170 point_values = extractor .extract (env_point_cloud , neighborhoods [target_index ], target_point_cloud ,
107- target_index + target_idx_base , volume )
171+ target_index + target_idx_base , volume )
108172 if n_features > 1 :
109173 for i in range (n_features ):
110174 feature_values [i ][target_index ] = point_values [i ]
111175 else :
112176 feature_values [0 ][target_index ] = point_values
113- for i in range (n_features ):
114- feature = provided_features [i ]
115- if (target_idx_base != 0 ):
116- target_point_cloud [keys .point ][feature ]["data" ] = np .append (target_point_cloud [keys .point ][feature ]["data" ], feature_values [i ])
117- elif (overwrite or (feature not in target_point_cloud [keys .point ])) and (target_idx_base == 0 ):
118- target_point_cloud [keys .point ][feature ] = {
119- "type" : 'float64' , "data" : feature_values [i ]}
120177
121178
122- def _make_feature_list (feature_names ):
123- feature_list = reversed (_make_feature_list_helper (feature_names ))
179+ def _add_or_update_feature_in_chunks (env_point_cloud , extractor , feature_values , n_features , n_targets , neighborhoods ,
180+ target_idx_base , target_point_cloud , volume ):
181+ chunk_size = 100000
182+ print ('calculating {} in chunks' .format (extractor .provides ()))
183+ for chunk_no in range (int (np .math .ceil (n_targets / chunk_size ))):
184+ i_start = chunk_no * chunk_size
185+ i_end = min ((chunk_no + 1 ) * chunk_size , n_targets )
186+ target_indices = np .arange (i_start , i_end )
187+ point_values = extractor .extract (env_point_cloud , neighborhoods [i_start :i_end ], target_point_cloud ,
188+ target_indices + target_idx_base , volume )
189+
190+ if n_features > 1 :
191+ for i in range (n_features ):
192+ feature_values [i ][target_indices ] = point_values [i ]
193+ else :
194+ feature_values [0 ][target_indices ] = point_values
195+
196+
197+ def _make_extended_feature_list (feature_names ):
198+ feature_list = reversed (_make_extended_feature_list_helper (feature_names ))
199+ return _remove_duplicates (feature_list )
200+
201+
202+ def _remove_duplicates (feature_list ):
124203 seen = set ()
125204 return [f for f in feature_list if not (f in seen or seen .add (f ))]
126205
127206
128- def _make_feature_list_helper (feature_names ):
207+ def _make_extended_feature_list_helper (feature_names ):
129208 feature_list = feature_names
130209 for feature_name in feature_names :
131210 extractor = FEATURES [feature_name ]()
132211 dependencies = extractor .requires ()
133212 feature_list .extend (dependencies )
134- feature_list .extend (_make_feature_list_helper (dependencies ))
213+ feature_list .extend (_make_extended_feature_list_helper (dependencies ))
135214 return feature_list
0 commit comments