You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib管道输出canvas.draw()到ffmpeg结果异常求助

问题与解决方案

我参考一段代码尝试将多幅Matplotlib绘图通过管道传入ffmpeg生成视频文件,但运行后plt.show()能显示正确的散点图,可ffmpeg生成的视频与显示结果存在明显差异。我推测问题出在f.canvas.draw()方法,但不知道如何查看该方法实际绘制的内容。

原代码

import numpy as np
import matplotlib.pyplot as plt
import subprocess

xlist = np.random.randint(100,size=100)
ylist = np.random.randint(100, size=100)
color = np.random.randint(2, size=100)

f = plt.figure(figsize=(5,5), dpi = 300)
canvas_width, canvas_height = f.canvas.get_width_height()
ax = f.add_axes([0,0,1,1])
ax.axis('off')


# Open an ffmpeg process
outf = 'ffmpeg.mp4'
cmdstring = ('ffmpeg',
    '-y', '-r', '30', # overwrite, 30fps
    '-s', '%dx%d' % (canvas_width, canvas_height), # size of image string
    '-pix_fmt', 'argb', # format
    '-f', 'rawvideo',  '-i', '-', # tell ffmpeg to expect raw video from the pipe
    '-vcodec', 'mpeg4', outf) # output encoding
p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE)

# Draw 1000 frames and write to the pipe
for frame in range(10):
    print("Working on frame")
    # draw the frame
    f = plt.figure(figsize=(5,5), dpi=300)
    ax = f.add_axes([0,0,1,1])
    ax.scatter(xlist, ylist,
               c=color, cmap = 'viridis')
    f.canvas.draw()
    plt.show()

    # extract the image as an ARGB string
    string = f.canvas.tostring_argb()
    # write to pipe
    p.stdin.write(string)

# Finish up
p.communicate()

问题分析

  1. 重复创建Figure:循环内每次重新创建figure和axes,导致ffmpeg初始化时指定的尺寸与后续实际绘制的canvas尺寸可能不匹配,进而引发视频画面异常。
  2. plt.show()干扰:该方法会启动GUI事件循环,可能改变canvas的绘制状态,导致后续提取的图像数据与显示内容不一致。
  3. 未清理旧内容:每次绘制新帧时没有清空之前的axes内容,可能导致画面叠加混乱。

解决方法

修改后的代码

import numpy as np
import matplotlib.pyplot as plt
import subprocess

xlist = np.random.randint(100,size=100)
ylist = np.random.randint(100, size=100)
color = np.random.randint(2, size=100)

# 仅初始化一次Figure和Axes,保证尺寸统一
f = plt.figure(figsize=(5,5), dpi=300)
ax = f.add_axes([0,0,1,1])
ax.axis('off')
canvas_width, canvas_height = f.canvas.get_width_height()

# 启动ffmpeg进程
outf = 'ffmpeg.mp4'
cmdstring = ('ffmpeg',
    '-y', '-r', '30',  # 覆盖已有文件,设置帧率30fps
    '-s', f'{canvas_width}x{canvas_height}',  # 匹配canvas尺寸
    '-pix_fmt', 'argb',  # 指定像素格式
    '-f', 'rawvideo', '-i', '-',  # 从标准输入读取原始视频
    '-vcodec', 'mpeg4', outf)  # 输出编码与文件名
p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE)

# 生成10帧视频
for frame in range(10):
    print(f"处理第{frame+1}帧")
    # 清空Axes内容,准备绘制新帧
    ax.clear()
    ax.scatter(xlist, ylist, c=color, cmap='viridis')
    ax.axis('off')  # 重置后重新关闭坐标轴
    
    # 将内容绘制到canvas
    f.canvas.draw()
    
    # 查看draw结果:保存为图片(取消注释即可)
    # plt.savefig(f'frame_{frame}.png', dpi=300, bbox_inches='tight', pad_inches=0)
    
    # 提取ARGB格式的图像数据并写入管道
    string = f.canvas.tostring_argb()
    p.stdin.write(string)

# 结束ffmpeg进程
p.communicate()
plt.close(f)  # 关闭Figure释放资源

查看f.canvas.draw()实际绘制内容

如果想确认draw()方法的输出,可以在f.canvas.draw()之后添加以下代码,将canvas内容保存为图片或转换为numpy数组查看:

# 方法1:保存为图片
plt.savefig('draw_result.png', dpi=300, bbox_inches='tight', pad_inches=0)

# 方法2:转换为numpy数组并显示
buf = f.canvas.buffer_rgba()
img_array = np.asarray(buf)
plt.imshow(img_array)
plt.show()

内容的提问来源于stack exchange,提问作者Narusan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:54:53