auto-mix-video / src /modules /extract_frame_dynamic.py
yuanxuchang
git init.
ea7ef05
Raw
History Blame Contribute Delete
3.78 kB
import cv2
import numpy as np
import torch
def extract_video_features(video_path, model, processor, device):
"""提取视频关键帧特征(智能抽帧版)
Args:
video_path: 视频文件路径
model: CLIP模型
processor: CLIP处理器
device: 计算设备
Returns:
features: 形状为 (n_frames, feature_dim) 的特征数组
timestamps: 对应帧的时间戳(秒)
"""
# 打开视频文件
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError(f"无法打开视频文件: {video_path}")
# 获取视频属性
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# 处理无效帧率
if fps <= 0:
fps = 30
print(f"警告: 视频帧率无效,使用默认值 {fps}")
# 智能抽帧参数设置
base_interval = max(1, int(fps * 2)) # 基础间隔(2秒)
min_interval = 1 # 最小间隔(1帧)
max_interval = max(1, int(fps * 5)) # 最大间隔(5秒)
motion_threshold = 15 # 运动阈值(像素平均变化)
window_size = 3 # 运动量平均窗口大小
# 初始化变量
prev_gray = None
motion_history = []
features = []
timestamps = []
current_index = 0
while current_index < total_frames:
# 设置当前帧位置
cap.set(cv2.CAP_PROP_POS_FRAMES, current_index)
ret, frame = cap.read()
if not ret:
break
try:
# 转换为灰度图计算运动量
current_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算运动量(与前一帧的差异)
if prev_gray is not None:
frame_diff = cv2.absdiff(current_gray, prev_gray) # 三个都是 (v_row, v_col)维
motion_level = np.mean(frame_diff)
motion_history.append(motion_level)
# 保持窗口大小
if len(motion_history) > window_size:
motion_history.pop(0)
# 计算平均运动量
avg_motion = np.mean(motion_history)
# 动态调整抽帧间隔
if avg_motion > motion_threshold:
next_interval = max(min_interval, base_interval - int(avg_motion/3))
else:
next_interval = min(max_interval, base_interval + int(base_interval * 0.5))
else:
next_interval = base_interval # 第一帧使用基础间隔
# 转换为RGB并提取特征
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
inputs = processor(images=frame_rgb, return_tensors="pt").to(device)
with torch.no_grad():
frame_features = model.get_image_features(**inputs)
# 保存结果
features.append(frame_features.cpu().numpy().squeeze())
timestamps.append(current_index / fps)
# 更新状态
prev_gray = current_gray
current_index += next_interval
except Exception as e:
print(f"处理帧 {current_index} 时出错: {str(e)}")
current_index += base_interval # 出错时使用基础间隔
continue
cap.release()
# 转换为numpy数组
if len(features) > 0:
features = np.vstack(features)
timestamps = np.array(timestamps)
else:
features = np.empty((0, 512)) # 空数组
timestamps = np.array([])
return features, timestamps, total_frames, fps