python 从视频中提取帧范围

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 从视频中提取帧范围相关的知识,希望对你有一定的参考价值。

#!/usr/bin/python3.6
import argparse
import collections
import json
import subprocess
import sys
from subprocess import Popen
from typing import List

from math import ceil

Rational = collections.namedtuple('Rational', ['num', 'den'])

def video_stream(ffp):
    return next(filter(lambda x: x['codec_type'] == 'video', ffp['streams']))


def fps(ffp) -> Rational:
    name = ffp['format']['format_name']
    vstream = video_stream(ffp)
    fps: str = vstream['avg_frame_rate'] if name != "mxf" else vstream['r_frame_rate']
    if fps is None:
        return None
    return parse_rational(fps)


def parse_rational(r: str) -> Rational:
    (num, den) = r.replace(":", "/").split("/")
    return Rational(int(num), int(den))


# for [1..255] return 256
# for 256 return 256
# for 0 return 0
def align16up(x: int) -> int:
    return x + 15 & (-15 - 1)


def ffmpeg_cmd(ffp, downscale: float = 2.0, startframe: int = 0, endframe_inclusive: int = -1) -> List[str]:
    vstream = video_stream(ffp)
    bps = int(vstream.get('bits_per_raw_sample', '8'))
    sarstr = vstream.get('sample_aspect_ratio', '1/1')
    sar = parse_rational(sarstr)

    duration: int = int(vstream['nb_frames'])
    assert duration > 0, "video has zero frames"
    if endframe_inclusive > -1:
        endframe_inclusive = min(duration - 1, endframe_inclusive)
    duration = min(duration, endframe_inclusive - startframe + 1)
    ss = 0
    if startframe > 0:
        _fps = fps(ffp)
        ss = startframe * _fps.den / _fps.num

    pix_fmt = "bgr48le" if bps > 8 else "bgr24"  # cv2.imread reads images in BGR order
    width = int(vstream['width'])
    height = int(vstream['height'])

    interlaced = False  # TODO: vstream['..']
    # fix non-square pixel videos
    width = ceil(width * sar.num / sar.den)

    width16 = align16up(width)
    height16 = align16up(height)

    if downscale != 1:
        width16 = align16up(int(width / downscale))
        height16 = align16up(int(height / downscale))

    videopath = ffp['format']['filename']

    cmd: List[str] = ['ffmpeg']
    if ss > 0:
        cmd.extend(['-ss', str(ss)])
    # if format/codec combination is seekable -ss should come BEFORE -i, otherwise slow seek will happen
    cmd.extend(['-i', videopath])

    if endframe_inclusive > -1:
        cmd.extend(['-vframes', str(duration)])

    if interlaced:
        yadif = "yadif,"
    else:
        yadif = ""
    if width != width16 or height != height16:
        cmd.extend(['-vf', "%sscale=%d:%d" % (yadif, width16, height16)])
    # cmd.extend(['-pix_fmt', pix_fmt, '-f', 'rawvideo', '-'])
    cmd.extend(['-pix_fmt', pix_fmt, '-f', 'image2', '-start_number', str(startframe), '%03d.png'])

    return cmd


parser = argparse.ArgumentParser(description='extract frames')
parser.add_argument('--input_path', type=str, required=True)
parser.add_argument('--start_frame', type=int, default=0, help='end frame (inclusive)')
parser.add_argument('--end_frame', type=int, default=-1, help='end frame (inclusive)')

cfg = parser.parse_args()

process: Popen = subprocess.Popen(
    ['ffprobe', '-v', 'quiet', '-i', cfg.input_path, '-show_streams', '-show_format', '-print_format', 'json'],
    stdout=subprocess.PIPE)
ffpjson = process.communicate()[0]
ffp = json.loads(ffpjson)

print(' '.join(ffmpeg_cmd(ffp, 1.0, cfg.start_frame, cfg.end_frame)))

以上是关于python 从视频中提取帧范围的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 python 和 openCV 从 .yuv 视频文件 (YUV420) 中提取帧?

在 Python 中提取视频帧

从 .FLA adobe 视频中提取帧

无法使用 C++ 和 OpenCV 从视频中提取帧

如何用python实现视频关键帧提取并保存为图片

从视频中提取帧的最快方法