Spaces:
Sleeping
Sleeping
| """eGeMAPS feature extraction using OpenSMILE.""" | |
| import logging | |
| import numpy as np | |
| import opensmile | |
| from src.features.schemas import EGeMAPSFeatures | |
| logger = logging.getLogger(__name__) | |
| class EGeMAPSExtractor: | |
| """Extract eGeMAPS features using OpenSMILE.""" | |
| def __init__(self, feature_set: str = "eGeMAPSv02", feature_level: str = "Functionals"): | |
| """ | |
| Initialize eGeMAPS extractor. | |
| Args: | |
| feature_set: Feature set name (eGeMAPSv02, GeMAPSv01b, etc.) | |
| feature_level: Feature level (Functionals or LowLevelDescriptors) | |
| """ | |
| self.feature_set = feature_set | |
| self.feature_level = feature_level | |
| try: | |
| self.smile = opensmile.Smile( | |
| feature_set=opensmile.FeatureSet[feature_set], | |
| feature_level=opensmile.FeatureLevel[feature_level], | |
| ) | |
| logger.info(f"OpenSMILE initialized: {feature_set}/{feature_level}") | |
| except Exception as e: | |
| logger.warning(f"OpenSMILE initialization failed: {e}") | |
| self.smile = None | |
| def extract(self, waveform: np.ndarray, sr: int) -> EGeMAPSFeatures: | |
| """ | |
| Extract eGeMAPS features. | |
| Args: | |
| waveform: Audio waveform | |
| sr: Sample rate | |
| Returns: | |
| EGeMAPSFeatures with 88-dimensional feature vector | |
| """ | |
| logger.debug("Extracting eGeMAPS features") | |
| if self.smile is None: | |
| logger.warning("OpenSMILE not available, using placeholder features") | |
| return self._placeholder_features() | |
| try: | |
| # Process audio | |
| features_df = self.smile.process_signal(waveform, sr) | |
| # Extract feature values (first row, all columns) | |
| features = features_df.iloc[0].values.astype(np.float32) | |
| # Get feature names | |
| feature_names = list(features_df.columns) | |
| # Ensure 88 dimensions (eGeMAPSv02 Functionals) | |
| if len(features) != 88: | |
| logger.warning( | |
| f"Expected 88 eGeMAPS features, got {len(features)}. Padding/truncating." | |
| ) | |
| if len(features) < 88: | |
| features = np.pad(features, (0, 88 - len(features)), mode="constant") | |
| else: | |
| features = features[:88] | |
| logger.info(f"Extracted eGeMAPS features: {len(features)} dims") | |
| return EGeMAPSFeatures(features=features, feature_names=feature_names) | |
| except Exception as e: | |
| logger.error(f"eGeMAPS extraction failed: {e}") | |
| return self._placeholder_features() | |
| def _placeholder_features(self) -> EGeMAPSFeatures: | |
| """Return placeholder eGeMAPS features.""" | |
| # Return zeros as placeholder | |
| features = np.zeros(88, dtype=np.float32) | |
| feature_names = [f"egemaps_{i}" for i in range(88)] | |
| logger.warning("Using placeholder eGeMAPS features (zeros)") | |
| return EGeMAPSFeatures(features=features, feature_names=feature_names) | |