3.3 音视频处理与分析 学习目标 掌握音频和视频数据的基本处理技术 理解特征提取的方法和原理 学会使用深度学习模型进行音视频分析 掌握多模态融合的技术实现 核心概念 音频处理:波形分析、频谱分析、梅尔频率倒谱系数 视频处理:帧提取、光流法、动作识别 特征提取:传统特征、深度学习特征 多模态融合:时间对齐、特征融合、决策融合 环境准备 / 前置知识 Python 3.8+ 音频处理库:librosa、soundfile 视频处理库:opencv-python、pyav 深度学习库:torch、transformers 数值计算:numpy、scipy 分步实战 步骤 1:音频基础预处理 音频预处理是多模态知识库构建的重要环节,直接影响后续特征提取的效果。
音频预处理是多模态知识库构建的重要环节,直接影响后续特征提取的效果。
import librosa import numpy as np import soundfile as sf from typing import Tuple, Optional class AudioPreprocessor: def __init__(self, sample_rate: int = 22050): self.sample_rate = sample_rate def load_audio(self, audio_path: str) -> Tuple[np.ndarray, int]: """加载音频文件""" try: audio, sr = librosa.load(audio_path, sr=self.sample_rate) return audio, sr except Exception as e: print(f"加载音频文件失败: {e}") return np.array([]), self.sample_rate def normalize_audio(self, audio: np.ndarray, method: str = 'peak') -> np.ndarray: """音频归一化""" if len(audio) == 0: return audio if method == 'peak': max_val = np.max(np.abs(audio)) if max_val > 0: return audio / max_val elif method == 'rms': rms = np.sqrt(np.mean(audio ** 2)) if rms > 0: return audio / rms return audio def remove_silence(self, audio: np.ndarray, threshold: float = 0.01, frame_length: int = 2048, hop_length: int = 512) -> np.ndarray: """去除音频中的静音部分""" if len(audio) == 0: return audio # 计算音频能量 rms = librosa.feature.rms(y=audio, frame_length=frame_length, hop_length=hop_length)[0] # 找到非静音部分 non_silent = rms > threshold non_silent_frames = np.where(non_silent)[0] if len(non_silent_frames) == 0: return audio[:1] # 转换为样本索引 start_sample = non_silent_frames[0] * hop_length end_sample = (non_silent_frames[-1] + 1) * hop_length end_sample = min(end_sample, len(audio)) return audio[start_sample:end_sample] def augment_audio(self, audio: np.ndarray, noise_factor: float = 0.005, time_shift: int = 1000, pitch_shift: int = 2) -> np.ndarray: """音频数据增强""" augmented = audio.copy() # 添加噪声 if noise_factor > 0: noise = np.random.normal(0, noise_factor, len(augmented)) augmented = augmented + noise # 时间偏移 if time_shift > 0: shift = np.random.randint(-time_shift, time_shift) if shift > 0: augmented = np.pad(augmented, (shift, 0), mode='constant')[:len(audio)] else: augmented = np.pad(augmented, (0, -shift), mode='constant')[-len(audio):] # 音高变换 if pitch_shift != 0: augmented = librosa.effects.pitch_shift(y=augmented, sr=self.sample_rate, n_steps=pitch_shift) return augmented # 使用示例 preprocessor = AudioPreprocessor(sample_rate=22050) # 生成示例音频 audio = np.random.randn(22050 * 2) # 2秒的随机音频 # 预处理流程 normalized = preprocessor.normalize_audio(audio, method='peak') denoised = preprocessor.remove_silence(normalized, threshold=0.005) augmented = preprocessor.augment_audio(denoised, noise_factor=0.001, pitch_shift=1) print(f"原始音频长度: {len(audio)}") print(f"处理后音频长度: {len(augmented)}") print(f"音频预处理完成")
音频特征提取是音频处理的核心,为后续的检索和分析提供数据基础。
import librosa import numpy as np from typing import List, Dict, Tuple class AudioFeatureExtractor: def __init__(self, sample_rate: int = 22050): self.sample_rate = sample_rate def extract_mfcc(self, audio: np.ndarray, n_mfcc: int = 13) -> np.ndarray: """提取MFCC特征""" if len(audio) == 0: return np.array([]) mfcc = librosa.feature.mfcc( y=audio, sr=self.sample_rate, n_mfcc=n_mfcc ) # 添加一阶和二阶差分 delta_mfcc = librosa.feature.delta(mfcc) delta2_mfcc = librosa.feature.delta(mfcc, order=2) # 组合特征 combined_features = np.concatenate([mfcc, delta_mfcc, delta2_mfcc], axis=0) return combined_features def extract_spectral_features(self, audio: np.ndarray) -> Dict[str, np.ndarray]: """提取频谱特征""" if len(audio) == 0: return {} # 计算短时傅里叶变换 stft = librosa.stft(audio) magnitude = np.abs(stft) # 计算频谱质心 spectral_centroid = librosa.feature.spectral_centroid(S=magnitude)[0] # 计算频谱带宽 spectral_bandwidth = librosa.feature.spectral_bandwidth(S=magnitude)[0] # 计算零交叉率 zero_crossing_rate = librosa.feature.zero_crossing_rate(audio)[0] return { 'spectral_centroid': spectral_centroid, 'spectral_bandwidth': spectral_bandwidth, 'zero_crossing_rate': zero_crossing_rate } def extract_chroma_features(self, audio: np.ndarray) -> np.ndarray: """提取色度特征""" if len(audio) == 0: return np.array([]) chroma = librosa.feature.chroma_stft( y=audio, sr=self.sample_rate ) return chroma def extract_all_features(self, audio: np.ndarray) -> Dict[str, np.ndarray]: """提取所有音频特征""" features = {} # 基础特征 features['mfcc'] = self.extract_mfcc(audio) features['spectral'] = self.extract_spectral_features(audio) features['chroma'] = self.extract_chroma_features(audio) # 统计特征 if len(audio) > 0: features['statistics'] = { 'mean': np.mean(audio), 'std': np.std(audio), 'max': np.max(np.abs(audio)), 'rms': np.sqrt(np.mean(audio ** 2)) } return features # 使用示例 extractor = AudioFeatureExtractor(sample_rate=22050) # 生成示例音频 audio = np.random.randn(22050 * 3) # 3秒的随机音频 # 提取各种特征 features = extractor.extract_all_features(audio) print("音频特征提取结果:") print(f"MFCC特征形状: {features['mfcc'].shape}") print(f"频谱质心形状: {features['spectral']['spectral_centroid'].shape}") print(f"色度特征形状: {features['chroma'].shape}") # 计算统计特征 if 'statistics' in features: stats = features['statistics'] print(f"统计特征: 均值={stats['mean']:.4f}, 标准差={stats['std']:.4f}")
视频处理需要考虑时间维度,通常需要对每一帧进行单独处理。
import cv2 import numpy as np from typing import List, Tuple, Optional class VideoPreprocessor: def __init__(self, target_size: Tuple[int, int] = (224, 224)): self.target_size = target_size def load_video(self, video_path: str) -> List[np.ndarray]: """加载视频文件""" frames = [] cap = cv2.VideoCapture(video_path) while cap.isOpened(): ret, frame = cap.read() if not ret: break frames.append(frame) cap.release() return frames def extract_frames(self, video_path: str, frame_interval: int = 1, max_frames: Optional[int] = None) -> List[np.ndarray]: """从视频中提取帧""" frames = [] cap = cv2.VideoCapture(video_path) frame_count = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break if frame_count % frame_interval == 0: frames.append(frame) if max_frames and len(frames) >= max_frames: break frame_count += 1 cap.release() return frames def preprocess_frame(self, frame: np.ndarray) -> np.ndarray: """预处理单个帧""" # 调整大小 if self.target_size: frame = self.resize_frame(frame) # 归一化 frame = frame.astype(np.float32) / 255.0 return frame def resize_frame(self, frame: np.ndarray, keep_aspect_ratio: bool = True) -> np.ndarray: """调整帧大小""" h, w = frame.shape[:2] target_w, target_h = self.target_size if not keep_aspect_ratio: return cv2.resize(frame, self.target_size) # 计算缩放比例 scale = min(target_w / w, target_h / h) new_w = int(w * scale) new_h = int(h * scale) # 调整大小 resized = cv2.resize(frame, (new_w, new_h)) # 创建目标大小的画布 if len(frame.shape) == 3: canvas = np.zeros((target_h, target_w, 3), dtype=np.uint8) else: canvas = np.zeros((target_h, target_w), dtype=np.uint8) # 居中放置 y_offset = (target_h - new_h) // 2 x_offset = (target_w - new_w) // 2 canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized return canvas def extract_motion_features(self, frames: List[np.ndarray]) -> Dict[str, np.ndarray]: """提取运动特征""" if len(frames) < 2: return {} # 计算帧间差分 motion_vectors = [] for i in range(1, len(frames)): prev_frame = cv2.cvtColor(frames[i-1], cv2.COLOR_BGR2GRAY) curr_frame = cv2.cvtColor(frames[i], cv2.COLOR_BGR2GRAY) # 计算光流 flow = cv2.calcOpticalFlowFarneback( prev_frame, curr_frame, None, pyr_scale=0.5, levels=3, winsize=15, iterations=3, poly_n=5, poly_sigma=1.2, flags=0 ) # 计算运动幅度 magnitude = np.sqrt(flow[:,:,0]**2 + flow[:,:,1]**2) motion_vectors.append(magnitude) # 统计运动特征 motion_vectors = np.array(motion_vectors) motion_features = { 'mean_motion': np.mean(motion_vectors), 'max_motion': np.max(motion_vectors), 'std_motion': np.std(motion_vectors), 'motion_energy': np.sum(motion_vectors) } return motion_features # 使用示例 video_processor = VideoPreprocessor(target_size=(224, 224)) # 生成示例视频帧 frames = [np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8) for _ in range(10)] # 预处理帧 processed_frames = [video_processor.preprocess_frame(frame) for frame in frames] print(f"预处理帧数: {len(processed_frames)}") print(f"帧形状: {processed_frames[0].shape}") # 提取运动特征 motion_features = video_processor.extract_motion_features(frames) print("运动特征:") for key, value in motion_features.items(): print(f"{key}: {value:.4f}")
实现音视频数据的融合分析,提高识别准确性。
import numpy as np from typing import List, Dict, Tuple, Optional import torch import torch.nn as nn class AudioVideoFusion: def __init__(self, audio_dim: int = 96, video_dim: int = 512, fusion_dim: int = 256): self.audio_dim = audio_dim self.video_dim = video_dim self.fusion_dim = fusion_dim # 特征提取器 self.audio_encoder = nn.Sequential( nn.Linear(audio_dim, fusion_dim), nn.ReLU(), nn.Dropout(0.1), nn.Linear(fusion_dim, fusion_dim) ) self.video_encoder = nn.Sequential( nn.Linear(video_dim, fusion_dim), nn.ReLU(), nn.Dropout(0.1), nn.Linear(fusion_dim, fusion_dim) ) # 注意力机制 self.attention = nn.Sequential( nn.Linear(fusion_dim * 2, fusion_dim), nn.Tanh(), nn.Linear(fusion_dim, 1), nn.Softmax(dim=0) ) # 融合层 self.fusion_layer = nn.Sequential( nn.Linear(fusion_dim * 2, fusion_dim), nn.ReLU(), nn.Dropout(0.1) ) def extract_audio_features(self, audio_data: np.ndarray) -> torch.Tensor: """提取音频特征""" features = np.random.randn(self.audio_dim) return torch.FloatTensor(features).unsqueeze(0) def extract_video_features(self, video_frames: List[np.ndarray]) -> torch.Tensor: """提取视频特征""" features = np.random.randn(self.video_dim) return torch.FloatTensor(features).unsqueeze(0) def fusion_attention(self, audio_features: torch.Tensor, video_features: torch.Tensor) -> torch.Tensor: """基于注意力的融合""" # 编码特征 audio_encoded = self.audio_encoder(audio_features) video_encoded = self.video_encoder(video_features) # 计算注意力权重 combined = torch.cat([audio_encoded, video_encoded], dim=1) attention_weights = self.attention(combined) # 加权融合 fused_features = attention_weights * audio_encoded + (1 - attention_weights) * video_encoded return fused_features def temporal_fusion(self, audio_sequence: List[torch.Tensor], video_sequence: List[torch.Tensor]) -> torch.Tensor: """时序融合""" if len(audio_sequence) != len(video_sequence): raise ValueError("音视频序列长度必须相同") fused_features = [] for audio_feat, video_feat in zip(audio_sequence, video_sequence): fused = self.fusion_attention(audio_feat, video_feat) fused_features.append(fused) # 时序聚合 fused_sequence = torch.stack(fused_features, dim=0) temporal_features = torch.mean(fused_sequence, dim=0) return temporal_features # 使用示例 fusion_model = AudioVideoFusion(audio_dim=96, video_dim=512, fusion_dim=256) # 模拟输入 audio_sequence = [torch.randn(1, 96) for _ in range(10)] # 10个音频特征 video_sequence = [torch.randn(1, 512) for _ in range(10)] # 10个视频特征 # 时序融合 temporal_fused = fusion_model.temporal_fusion(audio_sequence, video_sequence) print(f"时序融合特征形状: {temporal_fused.shape}")
import numpy as np from typing import List, Dict, Optional class MultimodalAudioVideoAnalyzer: def __init__(self, audio_sr: int = 22050, video_fps: int = 30): self.audio_sr = audio_sr self.video_fps = video_fps # 初始化组件 self.audio_preprocessor = AudioPreprocessor(sample_rate=audio_sr) self.video_preprocessor = VideoPreprocessor(target_size=(224, 224)) self.audio_extractor = AudioFeatureExtractor(sample_rate=audio_sr) self.fusion_model = AudioVideoFusion(audio_dim=96, video_dim=512, fusion_dim=256) def process_audio_video(self, audio_data: np.ndarray, video_frames: List[np.ndarray]) -> Dict: """处理音视频数据""" results = {} # 音频处理 audio_results = self._process_audio(audio_data) results['audio'] = audio_results # 视频处理 video_results = self._process_video(video_frames) results['video'] = video_results # 多模态融合 fusion_results = self._multimodal_fusion(audio_results, video_results) results['fusion'] = fusion_results return results def _process_audio(self, audio_data: np.ndarray) -> Dict: """处理音频数据""" results = {} # 预处理 normalized = self.audio_preprocessor.normalize_audio(audio_data) denoised = self.audio_preprocessor.remove_silence(normalized) # 特征提取 features = self.audio_extractor.extract_all_features(denoised) results['preprocessing'] = { 'normalized': normalized, 'denoised': denoised } results['features'] = features return results def _process_video(self, video_frames: List[np.ndarray]) -> Dict: """处理视频数据""" results = {} # 预处理 processed_frames = [self.video_preprocessor.preprocess_frame(frame) for frame in video_frames] # 运动特征 motion_features = self.video_preprocessor.extract_motion_features(video_frames) results['preprocessing'] = { 'processed_frames': processed_frames, 'frame_count': len(processed_frames) } results['motion_features'] = motion_features return results def _multimodal_fusion(self, audio_results: Dict, video_results: Dict) -> Dict: """多模态融合""" results = {} # 提取特征 audio_feat = torch.FloatTensor(np.random.randn(96)).unsqueeze(0) # 简化示例 video_feat = torch.FloatTensor(np.random.randn(512)).unsqueeze(0) # 简化示例 # 融合 fused_feat = self.fusion_model.fusion_attention(audio_feat, video_feat) results['fused_features'] = fused_feat.detach().numpy() return results # 使用示例 analyzer = MultimodalAudioVideoAnalyzer() # 生成示例数据 audio_data = np.random.randn(22050 * 5) # 5秒音频 video_frames = [np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8) for _ in range(150)] # 5秒视频 # 处理和分析 results = analyzer.process_audio_video(audio_data, video_frames) print("多模态音视频分析结果:") print(f"音频特征数量: {len(results['audio']['features'])}") print(f"视频帧数: {results['video']['frame_count']}") print(f"融合特征形状: {results['fusion']['fused_features'].shape}")
A:处理不同采样率的音频数据时,建议:
A:帧间隔的选择取决于应用场景:
A:时间对齐是关键问题,解决方案包括:
A:对于大规模数据处理:
本节详细介绍了音视频处理与分析的核心技术,涵盖了从基础预处理到高级融合分析的完整技术栈。主要内容包括:
这些技术为构建多模态知识库中的音视频处理模块提供了重要的技术支撑。
本章预计完成时间:60分钟 难度:高级