Files
yawiii-ComfyUI-Prompt-Assis…/utils/video.py
T
2025-12-21 22:52:38 +08:00

128 lines
4.1 KiB
Python

"""
视频帧提取工具模块
提供基于帧索引的精确帧提取功能
"""
import cv2
import base64
import os
from io import BytesIO
# ---帧提取核心功能---
def extract_frame_by_index(video_path, frame_index, force_rate=0):
"""
从视频中提取指定帧索引的图像
Args:
video_path: 视频文件路径
frame_index: 目标帧索引(基于 force_rate 后的帧序列)
force_rate: 强制帧率,0 表示使用原始帧率
Returns:
dict: {success, data (base64 JPEG), width, height} 或 {success, error}
"""
try:
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return {"success": False, "error": "无法打开视频文件"}
# 获取原始视频属性
original_fps = cap.get(cv2.CAP_PROP_FPS)
total_original_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if original_fps <= 0:
cap.release()
return {"success": False, "error": "无法获取视频帧率"}
# 计算实际要读取的原始帧位置
if force_rate > 0 and force_rate != original_fps:
# force_rate 会改变帧采样:从原始帧中按比例抽取
# 帧索引 i 对应的原始帧位置 = i * (original_fps / force_rate)
actual_frame_pos = int(frame_index * (original_fps / force_rate))
else:
actual_frame_pos = frame_index
# 边界检查
if actual_frame_pos >= total_original_frames:
actual_frame_pos = total_original_frames - 1
if actual_frame_pos < 0:
actual_frame_pos = 0
# 定位并读取帧
cap.set(cv2.CAP_PROP_POS_FRAMES, actual_frame_pos)
ret, frame = cap.read()
cap.release()
if not ret or frame is None:
return {"success": False, "error": f"无法读取帧 {frame_index}"}
# BGR 转 RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 编码为 JPEG(降低质量加快传输)
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 60]
_, buffer = cv2.imencode('.jpg', cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR), encode_param)
# Base64 编码
base64_data = base64.b64encode(buffer).decode('utf-8')
return {
"success": True,
"data": base64_data,
"width": frame.shape[1],
"height": frame.shape[0],
"frame_index": frame_index,
"actual_frame_pos": actual_frame_pos
}
except Exception as e:
return {"success": False, "error": str(e)}
def get_video_frame_info(video_path, force_rate=0):
"""
获取视频帧相关信息
Args:
video_path: 视频文件路径
force_rate: 强制帧率
Returns:
dict: {success, original_fps, force_fps, total_frames, duration}
"""
try:
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return {"success": False, "error": "无法打开视频文件"}
original_fps = cap.get(cv2.CAP_PROP_FPS)
total_original_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
cap.release()
if original_fps <= 0:
return {"success": False, "error": "无法获取视频帧率"}
duration = total_original_frames / original_fps
# 计算 force_rate 后的实际帧数
if force_rate > 0:
actual_fps = force_rate
actual_total_frames = int(duration * force_rate)
else:
actual_fps = original_fps
actual_total_frames = total_original_frames
return {
"success": True,
"original_fps": original_fps,
"actual_fps": actual_fps,
"original_total_frames": total_original_frames,
"actual_total_frames": actual_total_frames,
"duration": duration
}
except Exception as e:
return {"success": False, "error": str(e)}