目录
视频选帧截取
split_video.py
python
import cv2
import os
import imageio
import numpy as np
from pathlib import Path
class VideoSplitter:
def __init__(self):
self.cap = None
self.video_path = ""
self.split_frame = -1
self.current_frame = 0
self.total_frames = 0
self.fps = 0
self.width = 0
self.height = 0
def load_video(self, video_path):
"""加载视频文件"""
if not os.path.exists(video_path):
print(f"错误: 视频文件不存在 - {video_path}")
return False
self.cap = cv2.VideoCapture(video_path)
if not self.cap.isOpened():
print("错误: 无法打开视频文件")
return False
self.video_path = video_path
self.total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"视频信息:")
print(f" 路径: {video_path}")
print(f" 总帧数: {self.total_frames}")
print(f" FPS: {self.fps:.2f}")
print(f" 分辨率: {self.width} x {self.height}")
return True
def save_video_part_with_imageio(self, start_frame, end_frame, output_path):
"""使用imageio保存视频片段"""
# 跳转到起始帧
self.cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
frames_to_save = []
total_frames_to_save = end_frame - start_frame + 1
print(f"正在读取和转换帧...")
for i in range(total_frames_to_save):
ret, frame = self.cap.read()
if not ret:
break
# 将BGR转换为RGB(imageio使用RGB格式)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames_to_save.append(frame_rgb)
# 显示进度
if i % 30 == 0: # 每30帧显示一次进度
print(f"读取进度: {i}/{total_frames_to_save} 帧")
if not frames_to_save:
print("错误: 没有可保存的帧")
return False
print(f"正在使用imageio保存视频...")
try:
# 使用imageio保存视频
# 设置fps和质量参数
with imageio.get_writer(
output_path,
fps=self.fps,
quality=8, # 质量参数,0-10,越高越好
codec='libx264' # 使用H.264编码
) as writer:
for i, frame in enumerate(frames_to_save):
writer.append_data(frame)
if i % 30 == 0:
print(f"保存进度: {i}/{len(frames_to_save)} 帧")
print(f"视频片段已保存: {output_path} (共 {len(frames_to_save)} 帧)")
return True
except Exception as e:
print(f"保存视频时出错: {e}")
return False
def save_video_part_mp4(self, start_frame, end_frame, output_path):
"""保存为MP4格式(使用imageio默认设置)"""
return self.save_video_part_with_imageio(start_frame, end_frame, output_path)
def split_video_at_frame(self, split_frame, output_format='mp4'):
"""在指定帧位置切割视频"""
if split_frame <= 0 or split_frame >= self.total_frames - 1:
print("错误: 切割帧必须在 1 到 总帧数-1 之间")
return False
self.split_frame = split_frame
# 生成输出文件名
video_dir = os.path.dirname(self.video_path)
video_name = Path(self.video_path).stem
if output_format.lower() == 'mp4':
# 第一部分:从开始到切割帧
part1_path = os.path.join(video_dir, f"{video_name}_part1.mp4")
# 第二部分:从切割帧+1到结束
part2_path = os.path.join(video_dir, f"{video_name}_part2.mp4")
save_function = self.save_video_part_mp4
else:
print(f"错误: 不支持的格式 {output_format}")
return False
print(f"开始切割视频...")
print(f"切割位置: 第 {split_frame} 帧")
print(f"输出格式: {output_format.upper()}")
# 保存第一部分 (0 到 split_frame)
print(f"\n保存第一部分: 帧 0-{split_frame}")
if not save_function(0, split_frame, part1_path):
return False
# 保存第二部分 (split_frame+1 到 最后)
print(f"\n保存第二部分: 帧 {split_frame +1}-{self.total_frames -1}")
if not save_function(split_frame + 1, self.total_frames - 1, part2_path):
return False
print(f"\n视频切割完成!")
print(f"第一部分: {part1_path}")
print(f"第二部分: {part2_path}")
return True
def frame_by_frame_playback(self):
"""一帧一帧播放视频"""
if not self.cap:
print("错误: 请先加载视频文件")
return -1
# 重置到第一帧
self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
self.current_frame = 0
cv2.namedWindow('Video_Splitter', cv2.WINDOW_NORMAL)
print("\n控制说明:")
print(" 'n' 或 右键: 下一帧")
print(" 'p' 或 左键: 上一帧")
print(" 's' : 在当前帧位置切割视频 (MP4格式)")
print(" 'g' : 在当前帧位置切割视频 (GIF格式)")
print(" 'j' : 跳转到指定帧")
print(" 'q' 或 ESC: 退出")
while True:
ret, frame = self.cap.read()
if not ret:
print("已到达视频末尾")
break
# 在画面上显示帧信息
info_text = f"Frame: {self.current_frame}/{self.total_frames -1}"
cv2.putText(frame, info_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 255, 0), 2)
cv2.putText(frame, "N:Next P:Prev S:Split(MP4) G:Split(GIF) J:Jump Q:Quit",
(10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
cv2.imshow('Video_Splitter', frame)
key = cv2.waitKey(0) & 0xFF
if key == ord('q') or key == 27: # q 或 ESC
break
elif key == ord('n') or key == 83: # n 或 右键
self.current_frame += 1
if self.current_frame >= self.total_frames:
self.current_frame = self.total_frames - 1
print("已到达最后一帧")
elif key == ord('p') or key == 81: # p 或 左键
self.current_frame -= 1
if self.current_frame < 0:
self.current_frame = 0
print("已到达第一帧")
elif key == ord('s'): # s 切割为MP4
if 0 < self.current_frame < self.total_frames - 1:
print(f"\n在第 {self.current_frame} 帧处切割视频为MP4格式")
success = self.split_video_at_frame(self.current_frame, 'mp4')
if success:
break
else:
print("错误: 不能在视频开头或结尾处切割")
elif key == ord('j'): # j 跳转
try:
target_frame = int(input("请输入要跳转的帧号: "))
if 0 <= target_frame < self.total_frames:
self.current_frame = target_frame
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame)
print(f"跳转到第 {target_frame} 帧")
else:
print(f"帧号必须在 0-{self.total_frames -1} 范围内")
except ValueError:
print("请输入有效的数字")
# 设置下一帧位置
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame)
cv2.destroyAllWindows()
return self.current_frame
if __name__ == "__main__":
"""主函数"""
splitter = VideoSplitter()
# 输入视频文件路径
video_path = r"D:\data\chantu\video_1106\chantu_1106\ok\20251106_1524_part001.mp4"
# 加载视频
if not splitter.load_video(video_path):
exit(12)
# 开始一帧一帧播放
final_frame = splitter.frame_by_frame_playback()
if splitter.split_frame != -1:
print(f"\n视频已在第 {splitter.split_frame} 帧处成功切割")
else:
print("\n视频未进行切割")
# 释放资源
if splitter.cap:
splitter.cap.release()