我想知道如何将PIL图像传输到FFMPY以将其保存为视频或gif,因为PIL库的量化方法在某些情况下有很大的质量损失.我首先用PIL做了一些修改,然后想要导出并保存结果.

除了一篇关于PIL to FFmpeg的帖子外,我在网上没有找到任何关于这个话题的信息: Pipe PIL images to ffmpeg stdin - Python个 我如何在FFMPY中实现类似的东西?

例如,如果我一开始就有这样的设置:

import ffmpy
import PIL
from PIL import Image as Img

images = [Img.open('frame 1.png'),Img.open('frame 2.png')]#How do I convert them to FFMPEG?

#Here I modify the images using PIL

#Save with FFMPEG:
ff = ffmpy.FFmpeg(
    inputs={images ?: None},#How do I insert PIL images here?
    outputs={'output.gif': None},
    executable='ffmpeg\\bin\\ffmpeg.exe')
ff.run()

如何使用FFMPY将图像转换并保存为视频? 可以通过在中间增加一些步骤来实现吗?我不想第一次将所有PIL图像保存为图像,然后导入它们并使用FFMPY第二次保存它们,因为对于较大的文件这将非常耗时.

推荐答案

根据ffmpy文档,似乎最相关的选项是使用using-pipe-protocol.

  • 与使用PIL读取图像不同,我们可以将PNG图像作为二进制数据读取到BytesIO(将所有图像读取到内存中的类似文件的对象):

     # List of input image files (assume all images are in the same resolution, and the same "pixel format").
     images = ['frame 1.png', 'frame 2.png', 'frame 3.png', 'frame 4.png', 'frame 5.png', 'frame 6.png', 'frame 7.png', 'frame 8.png']
    
     # Read PNG images from files, and write to BytesIO object in memory (read images as binary data without decoding).
     images_in_memory = io.BytesIO()
     for png_file_name in images:
         with open(png_file_name, 'rb') as f:
             images_in_memory.write(f.read())
    
  • Run ffmpy.FFmpeg using pipe protocol.
    Pass images_in_memory.getbuffer() as input_data argument to ff.run:

     ff = ffmpy.FFmpeg(
         inputs={'pipe:0': '-y -f image2pipe -r 1'},
         outputs={'output.gif': None},
         executable='\\ffmpeg\\bin\\ffmpeg.exe')
    
     # Write the entire buffer of encoded PNG images to the "pipe".
     ff.run(input_data=images_in_memory.getbuffer(), stdout=subprocess.PIPE)
    

The above solution seems a bit awkward, but it's the best solution I could find using ffmpy.
There are other FFmpeg to Python binding like ffmpeg-python, that supports writing the images one by one in a loop.
Using ffmpy, we have to read all the images into memory from advance.

The above solution keeps the PNG images in their encoded (binary form).
Instead of decoding the images with PIL (for example), FFmpeg is going to decode the PNG images.
Letting FFmpeg decode the images is more efficient, and saves memory.
The limitation is that all the images must have the same resolution.
The images also must have the same "pixel format" (all RGB or all RGBA but not a mix).
In case images have different resolution or pixels format, we have to decode the images (and maybe resize the images) using Python, and write images as "raw video".


为了进行测试,我们可以使用FFmpeg CLI创建PNG映像:

ffmpeg -f lavfi -i testsrc=size=192x108:rate=1:duration=8 "frame %d.png".


完整的代码示例:

import ffmpy
import io
import subprocess

#Building sample images using FFmpeg CLI for testing: ffmpeg -f lavfi -i testsrc=size=192x108:rate=1:duration=8 "frame %d.png"

# List of input image files (assume all images are in the same resolution, and the same "pixel format").
images = ['frame 1.png', 'frame 2.png', 'frame 3.png', 'frame 4.png', 'frame 5.png', 'frame 6.png', 'frame 7.png', 'frame 8.png']

# Read PNG images from files, and write to BytesIO object in memory (read images as binary data without decoding).
images_in_memory = io.BytesIO()
for png_file_name in images:
    with open(png_file_name, 'rb') as f:
        images_in_memory.write(f.read())

# Use pipe protocol: https://ffmpy.readthedocs.io/en/latest/examples.html#using-pipe-protocol
ff = ffmpy.FFmpeg(
    inputs={'pipe:0': '-y -f image2pipe -r 1'},
    outputs={'output.gif': None},
    executable='\\ffmpeg\\bin\\ffmpeg.exe')  # Note: I have ffmpeg.exe is in C:\ffmpeg\bin folder

ff.run(input_data=images_in_memory.getbuffer(), stdout=subprocess.PIPE)

Sample output output.gif:
enter image description here


最新情况:

使用来自Pillow的图像的相同解决方案:

如果我们将Pillow中的图像以PNG格式保存到BytesIO,则上述解决方案也有效.

示例:

import ffmpy
import io
import subprocess
from PIL import Image as Img

#Building sample images using FFmpeg CLI for testing: ffmpeg -f lavfi -i testsrc=size=192x108:rate=1:duration=8 "frame %d.png"

# List of input image files (assume all images are in the same resolution, and the same "pixel format").
images = ['frame 1.png', 'frame 2.png', 'frame 3.png', 'frame 4.png', 'frame 5.png', 'frame 6.png', 'frame 7.png', 'frame 8.png']

# Read PNG images from files, and write to BytesIO object in memory (read images as binary data without decoding).
images_in_memory = io.BytesIO()
for png_file_name in images:
    img = Img.open(png_file_name)
    # Modify the images using PIL...
    img.save(images_in_memory, format="png")

# Use pipe protocol: https://ffmpy.readthedocs.io/en/latest/examples.html#using-pipe-protocol
ff = ffmpy.FFmpeg(
    inputs={'pipe:0': '-y -f image2pipe -r 1'},
    outputs={'output.gif': None},
    executable='\\ffmpeg\\bin\\ffmpeg.exe')

ff.run(input_data=images_in_memory.getbuffer(), stdout=subprocess.PIPE)

在内存中将图像编码为PNG在执行时间上并不是最高效的,但它节省了内存空间.

Python相关问答推荐

跟踪我已从数组中 Select 的样本的最有效方法

我必须将Sigmoid函数与r2值的两种类型的数据集(每种6个数据集)进行匹配,然后绘制匹配函数的求导.我会犯错

Pytest两个具有无限循环和await命令的Deliverc函数

如何避免Chained when/then分配中的Mypy不兼容类型警告?

2D空间中的反旋算法

什么相当于pytorch中的numpy累积ufunc

有没有一种方法可以从python的pussompy比较结果中提取文本?

如何在Polars中从列表中的所有 struct 中 Select 字段?

如何合并两个列表,并获得每个索引值最高的列表名称?

Tkinter菜单自发添加额外项目

dask无groupby(ddf. agg([min,max])?''''

如何使用两个关键函数来排序一个多索引框架?

在用于Python的Bokeh包中设置按钮的样式

如何获得3D点的平移和旋转,给定的点已经旋转?

数据框,如果值在范围内,则获取范围和

Seaborn散点图使用多个不同的标记而不是点

利用SCIPY沿第一轴对数组进行内插

如何在Python中创建仅包含完整天数的月份的列表

使用pythonminidom过滤XML文件

Python:在cmd中添加参数时的语法