OpenSMILE因音频段过短返回NaN的问题求解
解决OpenSMILE处理超短音频段返回NaN的问题
核心原因
1/30秒(约33毫秒)的音频段长度远小于OpenSMILE默认的特征提取窗口(通常帧长20-50ms、移窗10ms),无法完成有效特征计算,因此返回全NaN数组。
可行解决方案
1. 调整音频段长度,匹配OpenSMILE最小输入要求
OpenSMILE提取LLD特征至少需要一帧以上的音频数据,建议保留视频30fps采样节奏的同时,扩展音频提取窗口:
- 视频仍每1/30秒取一帧
- 音频以当前视频帧时间戳为中心,扩展到至少50ms的时长
- 或合并相邻33ms音频段提取特征,再通过均值/插值映射到对应视频帧
示例代码调整(基于PyAV):
import av import opensmile import numpy as np smile = opensmile.Smile( feature_set=opensmile.FeatureSet.ComParE_2016, feature_level=opensmile.FeatureLevel.LowLevelDescriptors, ) container = av.open("input_video.mp4") video_stream = container.streams.video[0] audio_stream = container.streams.audio[0] target_frame_interval = 1/30 # 视频30fps间隔 audio_window_duration = 0.05 # 调整为50ms音频窗口 for video_frame in container.decode(video_stream): frame_ts = video_frame.time # 计算音频窗口的起止时间(以视频帧时间为中心) audio_start_ts = max(0, frame_ts - audio_window_duration/2) audio_end_ts = frame_ts + audio_window_duration/2 # 定位音频流并累积对应片段 container.seek(int(audio_start_ts * av.time_base), stream=audio_stream) audio_frames = [] for audio_frame in container.decode(audio_stream): if audio_frame.time > audio_end_ts: break audio_frames.append(audio_frame) if audio_frames: # 合并音频帧为numpy数组 audio_np = np.concatenate([f.to_ndarray() for f in audio_frames], axis=0) # 提取特征 features = smile.process_signal(audio_np, sample_rate=audio_stream.sample_rate) # 后续与视频MediaPipe特征拼接... else: # 处理视频开头/结尾的音频边界情况 pass
2. 修改OpenSMILE配置文件,降低最小窗口要求
若必须严格使用1/30秒音频段,可自定义配置缩小帧长和移窗:
- 复制OpenSMILE默认LLD配置文件(如
config/ComParE_2016.conf) - 修改
cFramer组件的frameSize和frameStep为对应33ms的样本数(如16000采样率下,33ms对应528样本) - 注意:过小窗口会降低特征鲁棒性,部分频域特征可能仍无法计算,需测试调整
示例配置片段修改:
[framer:cFramer] reader.dmLevel=wave writer.dmLevel=frame frameSize=528 # 33ms @ 16000Hz frameStep=528 frameCenterSpecial=left
3. 预提取全音频特征,插值映射到视频帧时间点
- 先提取整个音频流的LLD特征(用OpenSMILE默认窗口),得到带时间戳的特征序列
- 对每个30fps视频帧的时间点,用线性插值或最近邻匹配获取对应音频特征
- 这种方法既保证音频特征有效性,又能严格对齐视频采样节奏
示例代码思路:
# 先提取全音频的LLD特征 full_audio_np = [] for audio_frame in container.decode(audio_stream): full_audio_np.append(audio_frame.to_ndarray()) full_audio_np = np.concatenate(full_audio_np, axis=0) full_features = smile.process_signal(full_audio_np, sample_rate=audio_stream.sample_rate) # 遍历视频帧,插值匹配音频特征 for video_frame in container.decode(video_stream): frame_ts = video_frame.time # 找到相邻的音频特征时间点 prev_row = full_features[full_features['start'] <= frame_ts].iloc[-1] next_row = full_features[full_features['end'] >= frame_ts].iloc[0] # 线性插值得到当前帧对应的音频特征 interpolated_feature = np.interp( frame_ts, [prev_row['start'], next_row['end']], [prev_row.values[2:], next_row.values[2:]] # 跳过时间戳列 ) # 拼接视频MediaPipe特征...
验证建议
添加日志输出音频段的实际时长和样本数,确认满足OpenSMILE要求:
audio_duration = audio_end_ts - audio_start_ts sample_count = len(audio_np) print(f"Audio segment: {audio_duration:.3f}s, {sample_count} samples")
内容的提问来源于stack exchange,提问作者rashad
相关产品推荐
相关产品推荐

