本文目录导读:

我来介绍几种Python实现视频抽帧的方法,从简单到进阶都有。
使用OpenCV(最常用)
import cv2
import os
def extract_frames_opencv(video_path, output_dir, frame_interval=30, start_frame=0, end_frame=None):
"""
使用OpenCV抽取视频帧
Args:
video_path: 视频文件路径
output_dir: 输出目录
frame_interval: 抽帧间隔(每隔多少帧抽取一帧)
start_frame: 开始帧索引
end_frame: 结束帧索引
"""
# 创建输出目录
os.makedirs(output_dir, exist_ok=True)
# 打开视频文件
cap = cv2.VideoCapture(video_path)
# 获取视频信息
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print(f"视频FPS: {fps}, 总帧数: {total_frames}")
# 设置结束帧
if end_frame is None:
end_frame = total_frames
frame_count = 0
saved_count = 0
while True:
ret, frame = cap.read()
if not ret or frame_count >= end_frame:
break
# 按间隔抽帧
if frame_count >= start_frame and frame_count % frame_interval == 0:
# 保存帧
output_path = os.path.join(output_dir, f"frame_{saved_count:06d}.jpg")
cv2.imwrite(output_path, frame)
saved_count += 1
print(f"已保存第 {frame_count} 帧")
frame_count += 1
cap.release()
print(f"抽帧完成,共保存 {saved_count} 帧")
return saved_count
# 使用示例
extract_frames_opencv(
video_path="input_video.mp4",
output_dir="extracted_frames",
frame_interval=30, # 每秒抽1帧(假设30fps)
start_frame=0,
end_frame=300 # 只处理前300帧
)
按时间间隔抽帧
import cv2
import numpy as np
from datetime import timedelta
def extract_frames_by_time(video_path, output_dir, time_interval=1.0, max_frames=None):
"""
按时间间隔抽帧
Args:
video_path: 视频文件路径
output_dir: 输出目录
time_interval: 时间间隔(秒)
max_frames: 最大抽帧数量
"""
import os
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
# 计算间隔帧数
frame_interval = int(fps * time_interval)
frame_count = 0
saved_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_count % frame_interval == 0:
# 添加时间戳
time_sec = frame_count / fps
time_str = str(timedelta(seconds=int(time_sec)))
# 在帧上添加时间信息
frame_with_timestamp = frame.copy()
cv2.putText(frame_with_timestamp, f"Time: {time_str}",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1, (0, 255, 0), 2)
# 保存
output_path = os.path.join(output_dir, f"time_{time_str.replace(':', '-')}.jpg")
cv2.imwrite(output_path, frame_with_timestamp)
saved_count += 1
print(f"保存帧: {output_path}")
if max_frames and saved_count >= max_frames:
break
frame_count += 1
cap.release()
return saved_count
# 使用示例
extract_frames_by_time(
video_path="input_video.mp4",
output_dir="frames_by_time",
time_interval=5.0, # 每5秒抽一帧
max_frames=10
)
使用MoviePy(更简洁)
from moviepy.video.io.VideoFileClip import VideoFileClip
import os
def extract_frames_moviepy(video_path, output_dir, frame_interval=30):
"""
使用MoviePy抽取视频帧
Args:
video_path: 视频文件路径
output_dir: 输出目录
frame_interval: 抽帧间隔
"""
os.makedirs(output_dir, exist_ok=True)
# 加载视频
video = VideoFileClip(video_path)
# 获取视频信息
fps = video.fps
duration = video.duration
print(f"视频时长: {duration}秒, FPS: {fps}")
# 计算要抽取的帧数
total_frames = int(duration * fps)
frame_numbers = list(range(0, total_frames, frame_interval))
saved_count = 0
for frame_num in frame_numbers:
# 计算时间点
time_point = frame_num / fps
if time_point <= duration:
# 获取帧
frame = video.get_frame(time_point)
# 保存帧
output_path = os.path.join(output_dir, f"moviepy_frame_{saved_count:06d}.jpg")
# 使用PIL或OpenCV保存
from PIL import Image
import numpy as np
img = Image.fromarray(frame)
img.save(output_path)
saved_count += 1
print(f"已保存帧 {saved_count} at time {time_point:.2f}s")
video.close()
return saved_count
# 使用示例
extract_frames_moviepy(
video_path="input_video.mp4",
output_dir="moviepy_frames",
frame_interval=30
)
高级抽帧(场景变化检测)
import cv2
import numpy as np
import os
def smart_extract_frames(video_path, output_dir, threshold=30, min_gap=10):
"""
智能抽帧 - 基于场景变化检测
Args:
video_path: 视频文件路径
output_dir: 输出目录
threshold: 变化阈值
min_gap: 最小抽帧间隔(避免抽取过于相似的帧)
"""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = 0
saved_count = 0
prev_frame = None
last_saved_frame = 0
while True:
ret, frame = cap.read()
if not ret:
break
# 转换为灰度图用于比较
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if frame_count == 0:
# 第一帧直接保存
prev_frame = gray
cv2.imwrite(os.path.join(output_dir, f"smart_frame_{saved_count:06d}.jpg"), frame)
saved_count += 1
last_saved_frame = frame_count
else:
# 计算帧间差异
diff = cv2.absdiff(gray, prev_frame)
mean_diff = np.mean(diff)
# 如果变化超过阈值且与上次保存有足够间隔,保存帧
if mean_diff > threshold and (frame_count - last_saved_frame) >= min_gap:
output_path = os.path.join(output_dir, f"smart_frame_{saved_count:06d}.jpg")
cv2.imwrite(output_path, frame)
saved_count += 1
last_saved_frame = frame_count
print(f"场景变化: {saved_count} (帧{frame_count}, 差异值: {mean_diff:.2f})")
prev_frame = gray
frame_count += 1
# 可选:限制处理帧数
if frame_count > 1000:
break
cap.release()
print(f"智能抽帧完成,共保存 {saved_count} 帧")
return saved_count
# 使用示例
smart_extract_frames(
video_path="input_video.mp4",
output_dir="smart_frames",
threshold=30,
min_gap=15
)
批量处理多视频文件
import cv2
import os
import glob
from concurrent.futures import ThreadPoolExecutor
def process_single_video(video_path, output_base_dir, frame_interval=30):
"""
处理单个视频文件
"""
# 创建输出目录(按视频名称)
video_name = os.path.splitext(os.path.basename(video_path))[0]
output_dir = os.path.join(output_base_dir, video_name)
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
frame_count = 0
saved_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_count % frame_interval == 0:
output_path = os.path.join(output_dir, f"frame_{saved_count:06d}.jpg")
cv2.imwrite(output_path, frame)
saved_count += 1
frame_count += 1
cap.release()
return video_name, saved_count
def batch_extract_frames(video_dir, output_base_dir, frame_interval=30, max_workers=4):
"""
批量处理多个视频
Args:
video_dir: 视频文件目录
output_base_dir: 输出基础目录
frame_interval: 抽帧间隔
max_workers: 最大线程数
"""
# 获取所有视频文件
video_files = glob.glob(os.path.join(video_dir, "*.mp4"))
video_files += glob.glob(os.path.join(video_dir, "*.avi"))
video_files += glob.glob(os.path.join(video_dir, "*.mov"))
print(f"找到 {len(video_files)} 个视频文件")
# 多线程处理
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = []
for video_path in video_files:
future = executor.submit(
process_single_video,
video_path,
output_base_dir,
frame_interval
)
futures.append(future)
# 收集结果
for future in futures:
video_name, saved_count = future.result()
print(f"视频 {video_name}: 保存了 {saved_count} 帧")
# 使用示例
batch_extract_frames(
video_dir="videos/",
output_base_dir="extracted_frames/",
frame_interval=30,
max_workers=4
)
实用工具函数
import cv2
import json
from pathlib import Path
class VideoFrameExtractor:
"""视频帧提取器类"""
def __init__(self, video_path):
self.video_path = video_path
self.cap = cv2.VideoCapture(video_path)
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
def get_video_info(self):
"""获取视频信息"""
return {
'path': self.video_path,
'fps': self.fps,
'total_frames': self.total_frames,
'resolution': f"{self.width}x{self.height}",
'duration': self.total_frames / self.fps
}
def extract_frames(self, output_dir, method='interval', **kwargs):
"""
抽取帧的通用方法
Args:
output_dir: 输出目录
method: 抽帧方法 ('interval', 'time', 'smart')
**kwargs: 其他参数
"""
os.makedirs(output_dir, exist_ok=True)
if method == 'interval':
return self._extract_by_interval(output_dir, kwargs.get('frame_interval', 30))
elif method == 'time':
return self._extract_by_time(output_dir, kwargs.get('time_interval', 1.0))
elif method == 'smart':
return self._smart_extract(output_dir, kwargs.get('threshold', 30))
else:
raise ValueError(f"未知的抽帧方法: {method}")
def _extract_by_interval(self, output_dir, frame_interval):
# 实现间隔抽帧
pass
def _extract_by_time(self, output_dir, time_interval):
# 实现时间抽帧
pass
def _smart_extract(self, output_dir, threshold):
# 实现智能抽帧
pass
def release(self):
self.cap.release()
# 使用示例
extractor = VideoFrameExtractor("input_video.mp4")
print(json.dumps(extractor.get_video_info(), indent=2))
安装依赖
# 基本安装 pip install opencv-python # 如果需要MoviePy pip install moviepy # 如果需要PIL pip install Pillow # 如果需要多线程处理 pip install futures
选择建议
- 一般用途:使用OpenCV方法(方法一)
- 需要时间精确控制:使用按时间间隔抽帧(方法二)
- 快速开发:使用MoviePy(方法三)
- 场景变化检测:使用智能抽帧(方法四)
- 批量处理:使用多线程批量处理(方法五)
每种方法都有其适用场景,你可以根据具体需求选择合适的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播