视频选帧截取

目录

视频选帧截取


视频选帧截取

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()
相关推荐
ZouZou老师1 小时前
视频编解码颜色空间:RGB与YUV全解析
音视频
hmbbcsm1 小时前
练习python题目小记(七)
开发语言·python
qq_356196951 小时前
day27pipeline管道@浙大疏锦行
python
噔噔噔噔@1 小时前
第一章、基础理论——第一节、软件测试概述
python·单元测试·压力测试
冷雨夜中漫步1 小时前
AI入坑之路——(1)搭建本地的Python与Jupyter开发环境
人工智能·python·jupyter
CRUD酱1 小时前
RabbitMQ是如何确保消息的可靠性的?
java·python·rabbitmq
天若有情6731 小时前
PyTorch与OpenCV 计算机视觉实战指南(入门篇)
pytorch·opencv·计算机视觉
sivdead1 小时前
Agent平台消息节点输出设计思路
后端·python·agent
盼哥PyAI实验室2 小时前
【超详细教程】Python 连接 MySQL 全流程实战
python·mysql·oracle