• [常用工具] Python视频解码库DeFFcode使用指北


    DeFFcode是一种跨平台的高性能视频帧解码器,通过内部封装ffmpeg,提供GPU解码支持,几行python代码就能够快速解码视频帧,并具有强大的错误处理能力。DeFFcode的APIs支持多种媒体流作为输入源,例如IP摄像机、常规多媒体文件、屏幕录制、图像序列、网络协议(例如 HTTP(s)、RTP/RSTP)等。由于FFmpeg的学习曲线非常陡峭,封装FFmpeg后的DeFFcode提供类似OpenCV-Python编码语法来帮助用户,使得在Python中学习、创建和开发基于FFmpeg的应用程序变得更加容易。DeFFcode的官方代码仓库见:deffcode。DeFFcode的官方文档见deffcode_doc

    DeFFcode的作者专注于音视频流的处理,除了Deffcode,作者还开源了Python视频处理库VidGear。VidGear的具体使用见Python视频处理库VidGear使用指北。DeFFcode还处于快速发展阶段,许多功能还需要完善。VidGear提供了比DeFFcode更丰富的视频处理接口,但是DeFFcode提供了比VidGear更高效更专业的视频解码接口。如果想要从事音视频流解码相关工作,还是学习ffmpeg的C++代码使用。

    入门ffmpeg使用或者想要对音视频处理有所了解推荐看看雷霄骅的博客。雷霄骅是视音频技术处理的专家,也是国内音视频领域无偿分享技术最多的程序员。但是很不幸雷霄骅因过度劳累于2016年与世长辞,所以大家还是多注意身体健康。身体才是革命的本钱,少加班,该休息就得休息,没有时间休息的人注定没有时间生病。

    1 前置知识

    1.1 安装

    对于DeFFcode,python版本需要高于3.7。DeFFcode支持以下系统:

    • 2016以后的linux版本,推荐使用linux系统运行VidGear
    • Windows 7及以上版本
    • MacOS 10.12.6及以上版本

    Deffcode安装代码如下:

    pip install -U deffcode

    特别要注意的是DeFFcode必须要安装ffmpeg执行文件。安装ffmpeg,其它系统自行搜索安装方法,ubuntu下直接输入:

    sudo apt install ffmpeg

    1.2 DeFFcode功能预览

    视频流解码

    DeFFcode核心功能就是利用ffmpeg进行视频解码。相关公开测试视频流地址为:

    • rtsp流:rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,来源于https://www.wowza.com/developer/rtsp-stream-test
    • http流:http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8,来源于苹果提供的测试源
    • http流:https://abhitronix.github.io/html/Big_Buck_Bunny_1080_10s_1MB.mp4,来源于DeFFcode
    from deffcode import FFdecoder
    import cv2
    
    # FFedecoder创建视频源和视频解码规则,formulate在ffmpeg中执行语句
    # 本地视频
    # decoder = FFdecoder("test.mp4").formulate()
    # rtsp流
    decoder = FFdecoder("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4").formulate()
    
    # 从decoder中抓取RGB图像
    for frame in decoder.generateFrame():
    
        print(frame.shape)
        # 将rgb图像转换为bgr图像,送给opencv展示
        frame_bgr = frame[:, :, ::-1]
        cv2.imshow("Output Frame", frame_bgr)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    # 安全关闭解码进程
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    视频流属性识别

    对于给定的输入源,DeFFcode使用各种方法识别视频流其中包含的文件所有属性,比如是否包含音频,图像分辨率,视频码率。不同的视频流返回的属性参数不同,具体按需要探索下就行了。

    from deffcode import Sourcer
    
    # sourcer设置定位视频流中的数据信息,probe_stream探测视频流的输出
    sourcer = Sourcer("test.mp4").probe_stream()
    
    # 解析为python字典数据
    data = sourcer.retrieve_metadata()
    # pretty_json表示解析为类似json.dump后的json字符串
    print(sourcer.retrieve_metadata(pretty_json=True))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2 基础使用

    2.1 解码视频文件

    DeFFcode的FFdecoder API很容易支持多媒体视频文件路径作为其source参数的输入。通过它的frame_format参数,您可以轻松解码所有知名计算机视觉库(例如 OpenCV)都支持的任何像素格式的视频帧。FFdecoder API 的generateFrame()函数可用于多种方法来访问来自给定源的RGB帧,例如生成器(推荐方法)、调用with语句和迭代器。在下面示例中,我们将使用上述访问方法从给定的视频文件中解码默认的RGB24视频帧。

    生成器调用

    from deffcode import FFdecoder
    
    decoder = FFdecoder("test.mp4").formulate()
    
    # 读取RGB24图像
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        print(frame.shape) 
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    with调用

    调用with语句方法可用于使代码更简单、更清晰、更易读。这种方法还自动处理FFdecoder API 中的formulate()和terminate()方法的管理,因此不需要显式调用它们。

    
    from deffcode import FFdecoder
    import cv2
    
    # 不需要调用formulate和terminate
    with FFdecoder("test.mp4") as decoder:
    
        for frame in decoder.generateFrame():
    
            if frame is None:
                break
    
            print(frame.shape)  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    迭代器调用

    迭代器的调用方式类似于OpenCV-Python读取视频的方式。

    
    from deffcode import FFdecoder
    
    decoder = FFdecoder("test.mp4").formulate()
    
    while True:
    
        # next返回迭代器的下一个项目
        frame = next(decoder.generateFrame(), None)
    
        if frame is None:
            break
    
        print(frame.shape) 
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    参数设置

    # 设置解码后的图像为bgr24,可以直接给opencv使用
    FFdecoder("test.mp4", frame_format="bgr24")
    # 设置解码后的图像为灰度图像,verbose输出解码的详细统计信息
    FFdecoder("test.mp4", frame_format="gray", verbose=True)
    # 设置解码后的图像为yuv420p格式,verbose输出解码的详细统计信息
    FFdecoder("test.mp4", frame_format="yuv420p", verbose=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2 解码本地摄像头和屏幕截取

    这部分不同平台使用方法不同,而且涉及到很多参数的使用和软件安装,所以这里推荐自行阅读Decoding Live Feed Devices

    2.3 解码网络流

    与解码视频文件类似,DeFFcode 的 FFdecoder API直接支持具有特定协议(如RTSP/RTP、HTTP(s)、MPEG-TS 等)的网络流作为其source参数的输入。以下示例用的都是网络上的公开视频流,由于网速问题有可能连接不上。

    http流解码

    from deffcode import FFdecoder
    import cv2
    
    # 获得BGR24图像
    # decoder = FFdecoder("ttp://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8", frame_format="bgr24").formulate()
    decoder = FFdecoder("https://abhitronix.github.io/html/Big_Buck_Bunny_1080_10s_1MB.mp4", frame_format="bgr24").formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    RTSP/RTP流解码

    
    from deffcode import FFdecoder
    import cv2
    
    # 设置传输协议为tcp
    ffparams = {"-rtsp_transport": "tcp"}
    
    # 取流
    decoder = FFdecoder("rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4", frame_format="bgr24", verbose=True, **ffparams).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2.4 从图像序列捕获帧

    特定命名图像序列读取

    下面的代码展示了如何带有特定数字标记的图像序列逐帧读取图像。您可以使用以下FFmpeg命令从视频文件中提取时间长度为2s的图像序列,注意图像保存的文件夹路径应该要预先创建。

    ffmpeg -t 2 -i test.mp4 imgs/image%02d.png

    from deffcode import FFdecoder
    import cv2
    
    # 设置特定数字开始读图,在本例为img01.png
    ffparams = {"-ffprefixes":["-start_number", "1"]}
    # 注意图像数大于三张
    # img%02d.png: 格式化输出文件名,本示例中输出img00.png,img01.png, img02.png等
    # 如果是jpeg图像序列,图像后缀名应该为jpeg而不是jpg
    decoder = FFdecoder("imgs/img%02d.png", frame_format="bgr24", verbose=True, **ffparams).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    glob 模式

    如果图像是连续的,但不一定是数字顺序,则通配符(*表示任意数量的任意字符)很有用,但是以下代码无法在windows下使用。

    from deffcode import FFdecoder
    import cv2
    
    # glob模式抓取图像
    # glob模式在 Windows FFmpeg 版本上不可用。
    ffparams = {"-ffprefixes":["-pattern_type", "glob"]}
    
    decoder = FFdecoder("imgs/img*.png", frame_format="bgr24", verbose=True, **ffparams).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    循环读取图像

    下面的设置展示了从单个或者多个图像循环读取的示例。注意jpg图像都以jpeg后缀命名。

    #  `-loop 1` 表示循环读取,loop是bool类型
    ffparams = {"-ffprefixes":["-loop", "1"]}
    
    # 设置单张图像循环读取
    decoder = FFdecoder("imgs/img01.png", frame_format="bgr24", verbose=True, **ffparams).formulate()
    
    # 设置多张图像循环读取
    decoder = FFdecoder("imgs/img%02d.png", frame_format="bgr24", verbose=True, **ffparams).formulate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.5 保存视频

    通过OpenCV保存视频

    from deffcode import FFdecoder
    import json, cv2
    
    decoder = FFdecoder("test.mp4", frame_format="bgr24").formulate()
    
    # decoder.metadata读取视频属性json数据,并转码为字典
    metadata_dict = json.loads(decoder.metadata)
    
    FOURCC = cv2.VideoWriter_fourcc("M", "J", "P", "G")
    FRAMERATE = metadata_dict["source_video_framerate"]
    FRAMESIZE = tuple(metadata_dict["source_video_resolution"])
    
    writer = cv2.VideoWriter("output.avi", FOURCC, FRAMERATE, FRAMESIZE)
    
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
        writer.write(frame)
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    writer.release()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    通过VidGear保存视频(推荐)

    使用这种方式保存视频,视频文件压缩率更高,保存速度更快,但是也CPU利用率也更高。

    from deffcode import FFdecoder
    from vidgear.gears import WriteGear
    import json
    
    decoder = FFdecoder("test.mp4", frame_format="bgr24", verbose=True).formulate()
    
    output_params = {
        "-input_framerate": json.loads(decoder.metadata)["source_video_framerate"]
    }
    
    writer = WriteGear(output_filename="output.mp4", **output_params)
    
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        writer.write(frame)
    
    decoder.terminate()
    
    writer.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    参数设置

    以下是各种ffmpeg参数的设置方法,ffparams设置参数后,然后传入FFdecoder。

    # 截取前3s视频,按倒序保存
    ffparams = {
        "-vf": "trim=end=7,reverse" 
    }
    
    # 裁剪中央输入区域,宽高都为输入视频的2/3,然后拉伸为原图像尺寸
    ffparams = {
        "-vf": "crop=2/3*in_w:2/3*in_h"
    }
    
    # 逆时针旋转图像30度,用绿色填充旋转图像未覆盖的区域
    ffparams = {
        "-vf": "trim=end=7,rotate=angle=-30*PI/180:fillcolor=green" 
    }
    
    # 保存前7秒视频,逆时针旋转90度,保持纵向布局
    # dir为旋转方向,具体可以搜搜ffmpeg transpose
    ffparams = {
        "-vf": "trim=end=7,transpose=dir=2:passthrough=portrait" 
    }
    
    # 水平翻转,然后缩放图像到其原始大小的一半
    ffparams = {
        "-vf": "hflip,scale=w=iw/2:h=ih/2" 
    
    }
    
    # 设置参数
    decoder = FFdecoder(
        "test.mp4", frame_format="bgr24", verbose=True, **ffparams
    ).formulate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.6 特定帧存储

    DeFFcode的FFdecoder API使用FFmpeg参数-ss提供轻松且精确的帧搜索,使我们能够从输入源的特定部分保存图像。

    from deffcode import FFdecoder
    from PIL import Image
    
    # 定义FFmpeg参数以查找00:00:01.45处图像,并获得一帧图像
    ffparams = {"-ss": "00:00:01.45", "-frames:v": 1}
    
    # 初始化参数
    decoder = FFdecoder("test.mp4", **ffparams).formulate()
    
    # 读取图像
    frame = next(decoder.generateFrame(), None)
    
    # 保存图像
    if not (frame is None):
        im = Image.fromarray(frame)
        im.save("test.png")
    else:
        raise ValueError("Something is wrong!")
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3 进阶使用

    3.1 虚拟源生成与解码

    DeFFcode提供各种创建虚拟视频流的示例,具体使用见Decoding Live Virtual Sources,这里只列出两个经典的案例。

    从测试源模式生成和解码帧

    testsrc图生成一个测试视频模式,显示颜色模式、滚动渐变和时间戳。这对于测试目的很有用。

    from deffcode import FFdecoder
    import cv2
    
    # 定义参数
    ffparams = {
        # 播放时间为10秒
        "-ffprefixes": ["-t", "10"],  
    }
    
    # 生成尺寸为1280x720,帧率30的testsrc测试图像
    decoder = FFdecoder(
        "testsrc=size=1280x720:rate=30",
        source_demuxer="lavfi",
        frame_format="bgr24",
        **ffparams
    ).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
        
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    使用自定义文本效果从渐变生成和解码帧

    from deffcode import FFdecoder
    import cv2
    
    ffparams = {
        "-ffprefixes": ["-t", "15"],  # 15秒播放
        "-vf": "drawtext="  # 绘制文本
        + "text='%{localtime\:%X}':"  # 时间 (HH::MM::SS)
        + "fontfile='c\:\/windows\/fonts\/arial.ttf':"  # 字体
        + "x=(w-text_w)/2:y=h-40*t:"  # 向上滚动效果
        + "fontsize=50:"  # 字体大小
        + "fontcolor=white",  # 字体颜色
    }
    
    
    decoder = FFdecoder(
        "gradients=n=3",
        source_demuxer="lavfi",
        frame_format="bgr24",
        **ffparams
    ).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    3.2 硬件加速视频解码

    FFmpeg 提供对不同平台上不同支持的专用硬件的访问,以执行一系列与视频相关的任务,以更快地完成或使用更少的其他资源(特别是 CPU)。使用ffmpeg -decoders终端命令列出所有 FFmpeg 支持的解码器。可以看看具体ffmpeg支持本机哪种硬件解码。

    比如判断ffmpeg是否可以通过依赖于gpu cuda的h264_cuvid解码,可以输入以下指令。如果输出包含了h264_cuvid那么就是支持的,可以通过gpu加速解码。

    linux系统:ffmpeg -hide_banner -decoders | grep h264

    windows系统:ffmpeg -hide_banner -decoders | findstr h264

    如果支持h264_cuvid加速解码,可以尝试以下示例代码。

    from deffcode import FFdecoder
    import cv2
    
    ffparams = {
        "-vcodec": "h264_cuvid", # CUVID H.264加速视频解码
        "-ffprefixes": ["-vsync", "0"], # 视频同步方法,一般都是自动,这里设置为0
    }
    
    decoder = FFdecoder(
        "test.mp4", frame_format="bgr24", verbose=True, **ffparams
    ).formulate()
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3.3 复杂效果添加

    添加水印

    以下代码展示了如何在读取的视频中添加图像,并保存视频到本地。

    from deffcode import FFdecoder
    from vidgear.gears import WriteGear
    import json, cv2
    
    # 定义带有复杂水印的视频过滤器
    ffparams = {
        "-ffprefixes": ["-t", "5"],  # 视频总长度为5秒
        "-clones": [
            "-i",
            "watermark.png",  
        ],
        "-filter_complex": "[1]format=rgba,"  # 设置水印图像输入格式
        + "colorchannelmixer=aa=0.7[logo];"  # 设置水印透明度,数值越小越透明
        + "[0][logo]overlay=W-w-{pixel}:H-h-{pixel}:format=auto,".format(  
            pixel=5 # 设置水印图片在距离输入视频右下角5个像素处
        )
        + "format=bgr24",  # 设置输出格式
    }
    
    
    decoder = FFdecoder(
        "test.mp4", frame_format="bgr24", verbose=True, **ffparams
    ).formulate()
    
    
    output_params = {
        "-input_framerate": json.loads(decoder.metadata)["source_video_framerate"],
    }
    
    # 保存视频
    writer = WriteGear(output_filename="output.mp4", **output_params)
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        writer.write(frame)
    
    decoder.terminate()
    
    writer.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    图像效果混合

    下面的代码展示了如何往图像序列中混合虚拟效果。

    from deffcode import FFdecoder
    from vidgear.gears import WriteGear
    import cv2, json
    
    
    ffparams = {
        "-ffprefixes": [
            "-t", "10", # 视频长度为10s
            "-f", "lavfi", # 使用输入虚拟数据
            "-i", "mandelbrot=rate=25", # 视频帧率
        ],  
        "-custom_resolution": (1280, 720), # 重新设置图像 1280x720
        "-filter_complex":"[1:v]format=yuv444p[v1];" 
            + "[0:v]format=gbrp10le[v0];"
            + "[v1][v0]scale2ref[v1][v0];"
            + "[v0][v1]blend=all_mode='heat'," 
            + "format=yuv422p10le[v]",
        "-map": "[v]", 
    }
    
    # 设置图像序列路径
    decoder = FFdecoder(
        "./imgs/image-%03d.png", frame_format="bgr24", verbose=True, **ffparams
    ).formulate()
    
    output_params = {
        "-input_framerate": 25,  
    }
    
    writer = WriteGear(output_filename="output.mp4", **output_params)
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        writer.write(frame)
    
    decoder.terminate()
    
    writer.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    此外Deffcode还支持添加各种艺术效果,具体方法可以阅读transcode-art-filtergraphs

    3.4 视频属性数据更改

    添加新属性

    下面代码展示了读取视频后,往读取的属性数据中添加新的属性,注意该操作并不更改视频的实际属性数据。

    from deffcode import FFdecoder
    import json
    
    
    decoder = FFdecoder("test.mp4", verbose=True)
    
    # 设置字典数据
    data = dict(
        mystring="abcd", 
        myint=1234, 
        mylist=[1, "Rohan", ["inner_list"]], 
        mytuple=(1, "John", ("inner_tuple")), 
        mydict={"anotherstring": "hello"}, 
        myjson=json.loads('{"name": "John", "age": 30, "city": "New York"}'),
    )
    
    # 分配视频的属性数据
    decoder.metadata = data
    
    decoder.formulate()
    
    print(decoder.metadata)
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    修改已有视频属性

    在视频流解码前,可以设置视频流的属性数据,那么就会以更改后的属性解码图像。

    from deffcode import FFdecoder
    import cv2
    
    
    decoder = FFdecoder("test.mp4", verbose=True)
    
    # 替换属性数据,会以当前属性解码视频
    decoder.metadata = {
        "output_frames_pixfmt": "gray",  # 灰度图
        "source_video_resolution": [352, 288],  # 宽高更改为352,288
    }
    
    
    decoder.formulate()
    
    
    print(decoder.metadata)
    
    for frame in decoder.generateFrame():
    
        if frame is None:
            break
    
        cv2.imshow("Output gray", frame)
    
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break
    
    cv2.destroyAllWindows()
    
    decoder.terminate()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4 参考

  • 相关阅读:
    uniapp-地区的四级联动
    TCP多线程模型、IO模型(select、poll、epoll)
    【权威出版/投稿优惠】2024年智慧城市与信息化教育国际会议(SCIE 2024)
    压缩pdf文件的大小,pdf档怎么压缩为最小内存
    面试官:有了for循环 为什么还要forEach?
    终于等到你!Franka Emika新款机器人FP3震撼发布,带来全新生态,重新定义协作机器人!
    什么是运维自动化巡检?
    计算机毕业论文java毕业设计选题基于springboot的社区服务管理包运行成功]
    【Linux】Http协议
    驱动开发 day3 9/12
  • 原文地址:https://blog.csdn.net/LuohenYJ/article/details/126421757