如何写视频画面截取脚本

wen 实用脚本 21

本文目录导读:

如何写视频画面截取脚本

  1. Python + OpenCV(最常用)
  2. FFmpeg 命令行脚本(最快速)
  3. 带GUI的Python脚本
  4. 高级功能扩展
  5. 使用建议

我来帮你写一个视频画面截取脚本,以下是几种常用语言的实现方案:

Python + OpenCV(最常用)

import cv2
import os
from datetime import datetime
class VideoFrameExtractor:
    def __init__(self, video_path, output_dir="frames"):
        self.video_path = video_path
        self.output_dir = output_dir
        self.cap = cv2.VideoCapture(video_path)
        # 创建输出目录
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
    def get_video_info(self):
        """获取视频信息"""
        fps = self.cap.get(cv2.CAP_PROP_FPS)
        total_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
        duration = total_frames / fps
        return {
            'fps': fps,
            'total_frames': total_frames,
            'duration': duration
        }
    def extract_by_time(self, time_seconds, output_name=None):
        """按时间截取单帧"""
        # 定位到指定时间
        self.cap.set(cv2.CAP_PROP_POS_MSEC, time_seconds * 1000)
        ret, frame = self.cap.read()
        if ret:
            if output_name is None:
                output_name = f"frame_{time_seconds:.2f}s.jpg"
            output_path = os.path.join(self.output_dir, output_name)
            cv2.imwrite(output_path, frame)
            print(f"已保存: {output_path}")
            return True
        return False
    def extract_by_frame(self, frame_number, output_name=None):
        """按帧号截取"""
        self.cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
        ret, frame = self.cap.read()
        if ret:
            if output_name is None:
                output_name = f"frame_{frame_number}.jpg"
            output_path = os.path.join(self.output_dir, output_name)
            cv2.imwrite(output_path, frame)
            print(f"已保存: {output_path}")
            return True
        return False
    def extract_batch(self, times=None, frames=None, interval=None):
        """批量截取"""
        results = []
        # 按时间列表截取
        if times:
            for t in times:
                results.append(self.extract_by_time(t))
        # 按帧号列表截取
        if frames:
            for f in frames:
                results.append(self.extract_by_frame(f))
        # 按时间间隔截取
        if interval:
            video_info = self.get_video_info()
            current_time = 0
            while current_time < video_info['duration']:
                self.extract_by_time(current_time)
                current_time += interval
        return results
    def extract_keyframes(self):
        """提取关键帧(使用帧差法)"""
        prev_frame = None
        frame_count = 0
        keyframes = []
        while True:
            ret, frame = self.cap.read()
            if not ret:
                break
            if prev_frame is not None:
                # 计算帧差
                gray1 = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
                gray2 = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                diff = cv2.absdiff(gray1, gray2)
                mean_diff = diff.mean()
                # 如果帧差大于阈值,视为关键帧
                if mean_diff > 30:  # 阈值可调整
                    output_path = os.path.join(self.output_dir, 
                                              f"keyframe_{frame_count}.jpg")
                    cv2.imwrite(output_path, frame)
                    keyframes.append(output_path)
            prev_frame = frame
            frame_count += 1
        print(f"提取了 {len(keyframes)} 个关键帧")
        return keyframes
    def __del__(self):
        self.cap.release()
# 使用示例
if __name__ == "__main__":
    extractor = VideoFrameExtractor("video.mp4")
    # 获取视频信息
    info = extractor.get_video_info()
    print(f"视频信息: {info}")
    # 按时间截取
    extractor.extract_by_time(10.5)  # 10.5秒处
    # 按帧号截取
    extractor.extract_by_frame(300)  # 第300帧
    # 批量截取
    extractor.extract_batch(
        times=[5, 10, 15, 20, 25],
        frames=[100, 200, 500]
    )
    # 每隔5秒截取
    extractor.extract_batch(interval=5)
    # 提取关键帧
    keyframes = extractor.extract_keyframes()

FFmpeg 命令行脚本(最快速)

Windows (.bat)

@echo off
setlocal enabledelayedexpansion
:: 配置参数
set INPUT_VIDEO=video.mp4
set OUTPUT_DIR=frames
set FRAME_INTERVAL=5
:: 创建输出目录
if not exist %OUTPUT_DIR% mkdir %OUTPUT_DIR%
echo 开始提取视频帧...
:: 按时间间隔提取
ffmpeg -i %INPUT_VIDEO% -vf "fps=1/%FRAME_INTERVAL%" %OUTPUT_DIR%\frame_%%04d.jpg
:: 提取特定时间点的帧
ffmpeg -i %INPUT_VIDEO% -ss 00:00:10 -vframes 1 %OUTPUT_DIR%\frame_10s.jpg
ffmpeg -i %INPUT_VIDEO% -ss 00:01:30 -vframes 1 %OUTPUT_DIR%\frame_90s.jpg
echo 提取完成!
pause

Linux/macOS (.sh)

#!/bin/bash
# 配置参数
INPUT_VIDEO="video.mp4"
OUTPUT_DIR="frames"
FRAME_INTERVAL=5
# 创建输出目录
mkdir -p $OUTPUT_DIR
echo "开始提取视频帧..."
# 按时间间隔提取
ffmpeg -i $INPUT_VIDEO -vf "fps=1/$FRAME_INTERVAL" $OUTPUT_DIR/frame_%04d.jpg
# 提取特定时间点的帧
ffmpeg -i $INPUT_VIDEO -ss 00:00:10 -vframes 1 $OUTPUT_DIR/frame_10s.jpg
ffmpeg -i $INPUT_VIDEO -ss 00:01:30 -vframes 1 $OUTPUT_DIR/frame_90s.jpg
echo "提取完成!"

带GUI的Python脚本

import tkinter as tk
from tkinter import filedialog, messagebox
import cv2
import os
from pathlib import Path
class VideoFrameExtractorGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("视频画面截取工具")
        self.root.geometry("500x400")
        self.video_path = None
        self.output_dir = None
        self.create_widgets()
    def create_widgets(self):
        # 视频选择
        tk.Label(self.root, text="选择视频:").pack(pady=5)
        self.video_label = tk.Label(self.root, text="未选择", bg="white", width=50)
        self.video_label.pack(pady=5)
        tk.Button(self.root, text="浏览", command=self.select_video).pack(pady=5)
        # 输出目录
        tk.Label(self.root, text="输出目录:").pack(pady=5)
        self.output_label = tk.Label(self.root, text="未选择", bg="white", width=50)
        self.output_label.pack(pady=5)
        tk.Button(self.root, text="选择目录", command=self.select_output).pack(pady=5)
        # 截取方式
        tk.Label(self.root, text="截取方式:").pack(pady=10)
        # 帧号截取
        tk.Label(self.root, text="帧号 (用逗号分隔):").pack()
        self.frames_entry = tk.Entry(self.root, width=50)
        self.frames_entry.pack(pady=5)
        # 时间点截取
        tk.Label(self.root, text="时间点(秒, 用逗号分隔):").pack()
        self.times_entry = tk.Entry(self.root, width=50)
        self.times_entry.pack(pady=5)
        # 截取按钮
        tk.Button(self.root, text="开始截取", command=self.start_extraction,
                 bg="green", fg="white", height=2).pack(pady=20)
    def select_video(self):
        self.video_path = filedialog.askopenfilename(
            filetypes=[("视频文件", "*.mp4 *.avi *.mov *.mkv")]
        )
        if self.video_path:
            self.video_label.config(text=Path(self.video_path).name)
    def select_output(self):
        self.output_dir = filedialog.askdirectory()
        if self.output_dir:
            self.output_label.config(text=self.output_dir)
    def start_extraction(self):
        if not self.video_path or not self.output_dir:
            messagebox.showerror("错误", "请选择视频和输出目录")
            return
        cap = cv2.VideoCapture(self.video_path)
        # 解析帧号
        frames_text = self.frames_entry.get()
        if frames_text:
            frames = [int(f.strip()) for f in frames_text.split(",")]
            for frame_num in frames:
                cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
                ret, frame = cap.read()
                if ret:
                    cv2.imwrite(os.path.join(self.output_dir, f"frame_{frame_num}.jpg"), frame)
        # 解析时间点
        times_text = self.times_entry.get()
        if times_text:
            times = [float(t.strip()) for t in times_text.split(",")]
            for time_sec in times:
                cap.set(cv2.CAP_PROP_POS_MSEC, time_sec * 1000)
                ret, frame = cap.read()
                if ret:
                    cv2.imwrite(os.path.join(self.output_dir, f"frame_{time_sec}s.jpg"), frame)
        cap.release()
        messagebox.showinfo("完成", "截取完成!")
if __name__ == "__main__":
    root = tk.Tk()
    app = VideoFrameExtractorGUI(root)
    root.mainloop()

高级功能扩展

import numpy as np
def extract_with_preprocessing(frame, options=None):
    """添加预处理功能"""
    if options is None:
        options = {}
    # 调整大小
    if 'resize' in options:
        frame = cv2.resize(frame, options['resize'])
    # 灰度化
    if options.get('grayscale', False):
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 裁剪
    if 'crop' in options:
        x, y, w, h = options['crop']
        frame = frame[y:y+h, x:x+w]
    # 添加水印
    if 'watermark' in options:
        text = options['watermark']
        cv2.putText(frame, text, (10, 30), 
                    cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
    return frame
def batch_rename_frames(directory, prefix="frame", start=1):
    """批量重命名帧文件"""
    files = sorted([f for f in os.listdir(directory) 
                    if f.endswith(('.jpg', '.png', '.jpeg'))])
    for i, file in enumerate(files, start):
        ext = os.path.splitext(file)[1]
        new_name = f"{prefix}_{i:04d}{ext}"
        os.rename(os.path.join(directory, file),
                 os.path.join(directory, new_name))

使用建议

  1. 选择合适的方法

    • 需要GUI:使用Tkinter版本
    • 批处理:使用FFmpeg
    • 需要图像处理:使用OpenCV
  2. 性能优化

    • 使用FFmpeg处理大文件
    • 使用帧差法减少存储
  3. 格式兼容性

    • OpenCV支持常见格式
    • 确保安装相应的解码器
  4. 异常处理

    • 检查视频文件是否存在
    • 处理读取失败的帧

需要我详细解释某个特定功能或根据具体需求调整脚本吗?

抱歉,评论功能暂时关闭!