OpenCV 视频裁剪实现(保留原始视频编码和参数)

使用 OpenCV 实现视频裁剪,同时保留原始视频的编码器、帧率和其他参数。

import cv2
import numpy as np
import sys

def crop_video(input_path, output_path, crop_region):
    """
    裁剪视频并保留原始编码参数
    :param input_path: 输入视频路径
    :param output_path: 输出视频路径
    :param crop_region: 裁剪区域 (x, y, width, height)
    """
    # 打开视频文件
    cap = cv2.VideoCapture(input_path)
    if not cap.isOpened():
        print(f"错误: 无法打开视频文件 {input_path}")
        return False
    
    print(f"成功打开视频: {input_path}")
    
    # 获取原始视频参数
    original_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    original_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    codec = int(cap.get(cv2.CAP_PROP_FOURCC))
    codec_str = "".join([chr((codec >> 8 * i) & 0xFF) for i in range(4)])
    
    print("\n原始视频参数:")
    print(f"  分辨率: {original_width}x{original_height}")
    print(f"  帧率: {fps:.2f} FPS")
    print(f"  总帧数: {total_frames}")
    print(f"  编码器: {codec_str} (FourCC: {codec})")
    
    # 解析裁剪区域
    x, y, width, height = crop_region
    
    # 验证裁剪参数有效性
    if (x < 0 or y < 0 or 
        width <= 0 or height <= 0 or
        x + width > original_width or 
        y + height > original_height):
        print("\n错误: 裁剪区域超出视频边界")
        print(f"  视频尺寸: {original_width}x{original_height}")
        print(f"  裁剪区域: x={x}, y={y}, width={width}, height={height}")
        cap.release()
        return False
    
    print("\n裁剪区域参数:")
    print(f"  起始坐标: ({x}, {y})")
    print(f"  裁剪尺寸: {width}x{height}")
    print(f"  保留原始编码器: {codec_str}")
    
    # 创建视频编码器 - 使用原始编码器
    fourcc = cv2.VideoWriter_fourcc(*codec_str)
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
    
    if not out.isOpened():
        # 如果原始编码器失败,尝试使用MP4V作为备选
        print(f"警告: 无法使用原始编码器 {codec_str} 创建输出,尝试使用 'mp4v'")
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
        if not out.isOpened():
            print("错误: 无法创建输出视频文件")
            cap.release()
            return False
    
    frame_count = 0
    success = True
    
    print("\n开始处理视频...")
    while success:
        # 读取视频帧
        success, frame = cap.read()
        
        if not success:
            # 视频结束或读取失败
            break
        
        # 裁剪当前帧
        cropped_frame = frame[y:y+height, x:x+width]
        
        # 写入输出视频
        out.write(cropped_frame)
        
        frame_count += 1
        # 每处理10%的帧显示一次进度
        if frame_count % max(1, total_frames // 10) == 0 or frame_count == total_frames:
            progress = (frame_count / total_frames) * 100
            print(f"处理进度: {frame_count}/{total_frames} 帧 ({progress:.1f}%)")
    
    # 释放资源
    cap.release()
    out.release()
    cv2.destroyAllWindows()
    
    print(f"\n视频裁剪完成! 保存至: {output_path}")
    print(f"输出视频参数:")
    print(f"  分辨率: {width}x{height}")
    print(f"  帧率: {fps:.2f} FPS")
    print(f"  总帧数: {frame_count}")
    print(f"  编码器: {codec_str}")
    
    return True

def get_video_info(input_path):
    """获取视频信息而不进行处理"""
    cap = cv2.VideoCapture(input_path)
    if not cap.isOpened():
        print(f"错误: 无法打开视频文件 {input_path}")
        return
    
    # 获取原始视频参数
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    codec = int(cap.get(cv2.CAP_PROP_FOURCC))
    codec_str = "".join([chr((codec >> 8 * i) & 0xFF) for i in range(4)])
    
    print("\n视频信息:")
    print(f"  文件路径: {input_path}")
    print(f"  分辨率: {width}x{height}")
    print(f"  帧率: {fps:.2f} FPS")
    print(f"  总帧数: {total_frames}")
    print(f"  编码器: {codec_str} (FourCC: {codec})")
    
    cap.release()

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "-info":
        # 显示视频信息模式
        if len(sys.argv) < 3:
            print("用法: python video_crop.py -info <视频文件路径>")
            sys.exit(1)
        input_video = sys.argv[2]
        get_video_info(input_video)
        sys.exit(0)
    
    # 正常裁剪模式
    input_video = "input.mp4"       # 输入视频路径
    output_video = "output.mp4"     # 输出视频路径
    
    # 裁剪参数设置 (x, y, width, height)
    # 示例: 从(100, 150)开始,裁剪400x300的区域
    crop_region = (100, 150, 400, 300)
    
    # 执行裁剪
    result = crop_video(input_video, output_video, crop_region)
    
    if not result:
        print("\n裁剪过程失败,请检查错误信息")
        sys.exit(1)

功能特点

  1. 保留原始编码参数

    • 自动检测原始视频的FourCC编码器
    • 使用相同的编码器创建输出视频
    • 保留原始帧率、色彩空间等参数
  2. 详细的参数输出

    • 显示原始视频的分辨率、帧率、总帧数和编码器
    • 显示裁剪区域参数
    • 显示处理进度(每10%更新一次)
  3. 错误处理

    • 验证裁剪区域是否在视频范围内
    • 处理视频打开失败的情况
    • 如果原始编码器不可用,自动回退到mp4v编码器
  4. 辅助功能

    • 添加了-info参数来查看视频信息而不进行处理
    • 示例:python video_crop.py -info input.mp4

使用说明

  1. 安装依赖

    pip install opencv-python numpy
    
  2. 基本使用

    • 修改脚本中的input_videooutput_videocrop_region变量
    • 运行脚本:python video_crop.py
  3. 查看视频信息

    python video_crop.py -info input.mp4
    
  4. 自定义裁剪区域

    # 格式: (x, y, width, height)
    crop_region = (起始X坐标, 起始Y坐标, 裁剪宽度, 裁剪高度)
    

技术细节

  1. 保留原始编码器

    # 获取原始编码器的FourCC代码
    codec = int(cap.get(cv2.CAP_PROP_FOURCC))
    codec_str = "".join([chr((codec >> 8 * i) & 0xFF) for i in range(4)])
    
    # 使用相同的编码器创建输出
    fourcc = cv2.VideoWriter_fourcc(*codec_str)
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
    
  2. 边界检查

    if (x < 0 or y < 0 or 
        width <= 0 or height <= 0 or
        x + width > original_width or 
        y + height > original_height):
        # 显示错误信息并退出
    
  3. 进度显示

    # 每处理10%的帧显示一次进度
    if frame_count % max(1, total_frames // 10) == 0 or frame_count == total_frames:
        progress = (frame_count / total_frames) * 100
        print(f"处理进度: {frame_count}/{total_frames} 帧 ({progress:.1f}%)")
    

常见问题解决方案

  1. 输出视频无法播放

    • 尝试使用-info参数查看原始编码器
    • 如果原始编码器不支持,脚本会自动回退到mp4v
    • 可以手动指定编码器:修改fourcc = cv2.VideoWriter_fourcc(*'H264')
  2. 处理速度慢

    • 某些编码器(如H.264)处理速度较慢但文件小
    • 对于测试可以使用'mp4v''XVID'
  3. 裁剪区域不正确

    • 使用-info查看视频分辨率
    • 确保裁剪区域在视频范围内
    • 注意坐标系:左上角为(0,0),右下角为(width, height)
  4. 处理大视频内存不足

    • 脚本逐帧处理,内存占用低
    • 对于超大视频,可能需要增加系统可用内存
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐