【问题标题】:Can you "stream" images to ffmpeg to construct a video, instead of saving them to disk?您可以将图像“流式传输”到 ffmpeg 以构建视频,而不是将它们保存到磁盘吗?
【发布时间】:2012-10-28 23:54:55
【问题描述】:

我最近的工作涉及以编程方式制作视频。在 python 中,典型的工作流程如下所示:

import subprocess, Image, ImageDraw

for i in range(frames_per_second * video_duration_seconds):
    img = createFrame(i)
    img.save("%07d.png" % i)

subprocess.call(["ffmpeg","-y","-r",str(frames_per_second),"-i", "%07d.png","-vcodec","mpeg4", "-qscale","5", "-r", str(frames_per_second), "video.avi"])

此工作流程为视频中的每一帧创建一个图像并将其保存到磁盘。保存所有图像后,调用 ffmpeg 从所有图像构建视频。

将图像保存到磁盘(而不是在内存中创建图像)消耗了这里的大部分周期,并且似乎没有必要。有没有办法执行相同的功能,但不将图像保存到磁盘?因此,将调用 ffmpeg 并构建图像并在构建后立即将其提供给 ffmpeg。

【问题讨论】:

  • 我不知道您是如何创建图像的,但 ffmpeg 也接受管道输入:ffmpeg -f image2pipe -c:v png -r 30000/1001 -i -
  • 为简单起见,假设createFrame(i)返回一个Python图像库图像对象,我们将其存储在img中。我认为您的回应是朝着正确方向迈出的一步,但一半的挑战是在 python 程序中将构建的图像管道传输到 ffmpeg。
  • 也许排队,然后通过第二个线程管道图像?
  • 也许能够将您的输入发送到命名管道并将其传递给 ffmpeg,同样,过程基本相同......

标签: python image stream ffmpeg python-imaging-library


【解决方案1】:

我有点晚了,但是VidGear Python 库的WriteGear API 使用Hardware Encoders support 自动将 OpenCV 帧实时流水线化到任何平台上的 FFmpeg 中同时提供相同的 opencv-python 语法。这是一个基本的python示例:

# import libraries
from vidgear.gears import WriteGear
import cv2

output_params = {"-vcodec":"libx264", "-crf": 0, "-preset": "fast"} #define (Codec,CRF,preset) FFmpeg tweak parameters for writer

stream = cv2.VideoCapture(0) #Open live webcam video stream on first index(i.e. 0) device

writer = WriteGear(output_filename = 'Output.mp4', compression_mode = True, logging = True, **output_params) #Define writer with output filename 'Output.mp4' 

# infinite loop
while True:
    
    (grabbed, frame) = stream.read()
    # read frames

    # check if frame empty
    if not is grabbed:
        #if True break the infinite loop
        break
    

    # {do something with frame here}
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # write a modified frame to writer
    writer.write(gray) 
       
    # Show output window
    cv2.imshow("Output Frame", frame)

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

cv2.destroyAllWindows()
# close output window

stream.release()
# safely close video stream
writer.close()
# safely close writer

来源:https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/usage/#using-compression-mode-with-opencv

您可以查看VidGear Docs 了解更多高级应用程序和功能。

【讨论】:

  • 你能不能也用这个api写未压缩的视频,即将图像写入非压缩容器,以避免单独保存每个图像(这很慢)。
  • @matanster 是的,您可以使用 FFmpeg 本身做任何可能的事情。您可以在-vcodec 中使用r10kr210 等编码器来实现完全未压缩的AVI/MOV 视频或与其他特定编码器类似的任何内容:superuser.com/a/347434
  • 您甚至可以通过 URL 直接流式传输:abhitronix.github.io/vidgear/latest/gears/writegear/compression/…
【解决方案2】:

imageio 直接支持这个。它使用 FFMPEG 和 Video Acceleration API,速度非常快:

import imageio

writer = imageio.get_writer('video.avi', fps=fps)
for i in range(frames_per_second * video_duration_seconds):
    img = createFrame(i)
    writer.append_data(img)
writer.close()

【讨论】:

    【解决方案3】:

    好的,我得到了它的工作。感谢 LordNeckbeard 建议使用 image2pipe。我不得不使用 jpg 编码而不是 png because image2pipe with png doesn't work on my verision of ffmpeg。第一个脚本与您的问题代码基本相同,除了我实现了一个简单的图像创建,它只创建从黑色到红色的图像。我还添加了一些代码来计时执行。

    串行执行

    import subprocess, Image
    
    fps, duration = 24, 100
    for i in range(fps * duration):
        im = Image.new("RGB", (300, 300), (i, 1, 1))
        im.save("%07d.jpg" % i)
    subprocess.call(["ffmpeg","-y","-r",str(fps),"-i", "%07d.jpg","-vcodec","mpeg4", "-qscale","5", "-r", str(fps), "video.avi"])
    

    并行执行(没有图像保存到磁盘)

    import Image
    from subprocess import Popen, PIPE
    
    fps, duration = 24, 100
    p = Popen(['ffmpeg', '-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'mpeg4', '-qscale', '5', '-r', '24', 'video.avi'], stdin=PIPE)
    for i in range(fps * duration):
        im = Image.new("RGB", (300, 300), (i, 1, 1))
        im.save(p.stdin, 'JPEG')
    p.stdin.close()
    p.wait()
    

    结果很有趣,我运行每个脚本 3 次来比较性能: 序列号:

    12.9062321186
    12.8965060711
    12.9360799789
    

    平行:

    8.67797684669
    8.57139396667
    8.38926696777
    

    所以看起来并行版本的速度快了大约 1.5 倍。

    【讨论】:

    • 对于将来偶然发现此问题的任何人,将 'mjpeg' 替换为 'png' 并将 'JPEG' 替换为 'PNG' 对我使用 png 很有帮助。
    • 尽管文件大小为 x4,但我设法使用 -vcodec pngim.save(p.stdin, 'PNG') 获得了最佳质量
    • 该死,并行脚本运行良好,直到我更新到 Python 3.6。现在我在p = Popen(['ffmpeg',... 线上得到OSError: [WinError 6] The handle is invalid。任何已知的解决方法?
    • 找到了解决方案here。基本上,只需将stdout=PIPE 作为额外参数添加到Popen
    • 它应该是流式的而不是并行的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-14
    • 2011-07-01
    • 1970-01-01
    • 2019-10-13
    相关资源
    最近更新 更多