2010-11-04 34 views
58

Tôi muốn tạo phim h264 hoặc divx từ các khung mà tôi tạo trong một tập lệnh python trong matplotlib. Có khoảng 100 nghìn khung hình trong bộ phim này.Tạo phim từ python mà không lưu từng khung riêng lẻ vào các tệp

Trong ví dụ trên web [ví dụ: 1], tôi chỉ thấy phương pháp lưu từng khung hình thành png và sau đó chạy mencoder hoặc ffmpeg trên các tệp này. Trong trường hợp của tôi, việc lưu từng khung hình là không thực tế. Có cách nào để có một cốt truyện được tạo ra từ matplotlib và ống nó trực tiếp đến ffmpeg, tạo ra không có tập tin trung gian?

Lập trình với C-api của ffmpeg quá khó đối với tôi [ví dụ: 2]. Ngoài ra, tôi cần một mã hóa có nén tốt như x264 như các tập tin phim nếu không sẽ là quá lớn cho một bước tiếp theo. Vì vậy, nó sẽ là tuyệt vời để gắn bó với mencoder/ffmpeg/x264.

Có điều gì có thể thực hiện với ống [3] không?

[1] http://matplotlib.sourceforge.net/examples/animation/movie_demo.html

[2] How does one encode a series of images into H264 using the x264 C API?

[3] http://www.ffmpeg.org/ffmpeg-doc.html#SEC41

+0

Tôi chưa tìm ra cách để thực hiện việc này với thư viện hiện đang được duy trì ... (Tôi đã sử dụng pymedia trước đây, nhưng nó không còn được duy trì và sẽ không xây dựng trên bất kỳ hệ thống nào tôi sử dụng .. .) Nếu nó giúp, bạn có thể nhận được một bộ đệm RGB của một con số matplotlib bằng cách sử dụng 'buffer = fig.canvas.tostring_rgb()', và chiều rộng và chiều cao của hình trong pixel với 'fig.canvas.get_width_height()' (hoặc 'fig.bbox.width', v.v.) –

+0

OK, cảm ơn. Điều đó rất hữu ích. Tôi tự hỏi nếu một số chuyển đổi của bộ đệm có thể được piped để ffmpeg. pyffmpeg có một trình bao bọc Cython phức tạp, được cập nhật gần đây, để đọc khung avi theo khung. Nhưng không viết. Điều đó nghe giống như một nơi có thể để bắt đầu cho một người quen thuộc với thư viện ffmpeg. Ngay cả một cái gì đó giống như imlframe của MATLAB sẽ là tuyệt vời. – Paul

+1

Tôi đang chơi xung quanh với việc có ffmpeg đọc hoặc từ một đường ống đầu vào (với tùy chọn '-f image2pipe' để nó mong đợi một loạt ảnh) hoặc từ một ổ cắm cục bộ (ví dụ:' udp: // localhost: some_port') và viết cho các ổ cắm trong python ... Cho đến nay, chỉ một phần thành công ... Tôi cảm thấy như tôi gần như ở đó, mặc dù ... Tôi chỉ không đủ quen thuộc với ffmpeg ... –

Trả lời

43

Chức năng này hiện nay (ít nhất là 1.2.0, có thể 1.1) được nướng vào matplotlib thông qua lớp MovieWriter và các lớp con của nó trong mô-đun animation. Bạn cũng cần cài đặt trước ffmpeg.

import matplotlib.animation as animation 
import numpy as np 
from pylab import * 


dpi = 100 

def ani_frame(): 
    fig = plt.figure() 
    ax = fig.add_subplot(111) 
    ax.set_aspect('equal') 
    ax.get_xaxis().set_visible(False) 
    ax.get_yaxis().set_visible(False) 

    im = ax.imshow(rand(300,300),cmap='gray',interpolation='nearest') 
    im.set_clim([0,1]) 
    fig.set_size_inches([5,5]) 


    tight_layout() 


    def update_img(n): 
     tmp = rand(300,300) 
     im.set_data(tmp) 
     return im 

    #legend(loc=0) 
    ani = animation.FuncAnimation(fig,update_img,300,interval=30) 
    writer = animation.writers['ffmpeg'](fps=30) 

    ani.save('demo.mp4',writer=writer,dpi=dpi) 
    return ani 

Documentation for animation

20

Sau vá ffmpeg (xem Joe Kington bình luận cho câu hỏi của tôi), tôi đã có thể để có được của png đường ống để ffmpeg như sau:

import subprocess 
import numpy as np 
import matplotlib 
matplotlib.use('Agg') 
import matplotlib.pyplot as plt 

outf = 'test.avi' 
rate = 1 

cmdstring = ('local/bin/ffmpeg', 
      '-r', '%d' % rate, 
      '-f','image2pipe', 
      '-vcodec', 'png', 
      '-i', 'pipe:', outf 
      ) 
p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE) 

plt.figure() 
frames = 10 
for i in range(frames): 
    plt.imshow(np.random.randn(100,100)) 
    plt.savefig(p.stdin, format='png') 

Nó sẽ không hoạt động nếu không có patch, có tầm thường ifies hai tập tin và thêm libavcodec/png_parser.c. Tôi đã phải áp dụng bản vá theo cách thủ công cho libavcodec/Makefile. Cuối cùng, tôi đã xóa '-number' khỏi Makefile để xây dựng trang người đàn ông. Với tùy chọn biên dịch,

FFmpeg version 0.6.1, Copyright (c) 2000-2010 the FFmpeg developers 
    built on Nov 30 2010 20:42:02 with gcc 4.2.1 (Apple Inc. build 5664) 
    configuration: --prefix=/Users/paul/local_test --enable-gpl --enable-postproc --enable-swscale --enable-libxvid --enable-libx264 --enable-nonfree --mandir=/Users/paul/local_test/share/man --enable-shared --enable-pthreads --disable-indevs --cc=/usr/bin/gcc-4.2 --arch=x86_64 --extra-cflags=-I/opt/local/include --extra-ldflags=-L/opt/local/lib 
    libavutil  50.15. 1/50.15. 1 
    libavcodec 52.72. 2/52.72. 2 
    libavformat 52.64. 2/52.64. 2 
    libavdevice 52. 2. 0/52. 2. 0 
    libswscale  0.11. 0/0.11. 0 
    libpostproc 51. 2. 0/51. 2. 0 
+0

Thật tuyệt vời! +1 (Tôi chưa bao giờ có thể nhận ffmpeg chấp nhận luồng .png's, tôi nghĩ mình cần cập nhật phiên bản ffmpeg của mình ...) Và, trong trường hợp bạn đang băn khoăn, nó hoàn toàn có thể chấp nhận để đánh dấu câu trả lời của bạn câu trả lời cho câu hỏi của bạn. Xem thảo luận tại đây: http://meta.stackexchange.com/questions/17845/stack-overflow-etiquette-for-answering-your-own-question –

+0

Ok, tôi sẽ đánh dấu nó là đã trả lời. Cảm ơn lời khuyên lần nữa. – Paul

+0

Thật tuyệt vời. Tôi đã cố gắng làm điều tương tự. –

5

Điều này thật tuyệt! Tôi cũng muốn làm như vậy. Nhưng, tôi không bao giờ có thể biên dịch nguồn ffmpeg vá (0.6.1) trong Vista với môi trường MingW32 + MSYS + pr ... png_parser.c đã tạo ra Error1 trong quá trình biên dịch.

Vì vậy, tôi đã đưa ra giải pháp jpeg cho điều này bằng cách sử dụng PIL. Chỉ cần đặt ffmpeg.exe của bạn trong cùng thư mục với tập lệnh này. Điều này sẽ làm việc với ffmpeg mà không có bản vá dưới Windows. Tôi đã phải sử dụng phương pháp stdin.write chứ không phải là phương pháp giao tiếp được đề nghị trong các tài liệu chính thức về subprocess. Lưu ý rằng tùy chọn 2 -vcodec chỉ định codec mã hóa. Đường ống được đóng bởi p.stdin.close().

import subprocess 
import numpy as np 
from PIL import Image 

rate = 1 
outf = 'test.avi' 

cmdstring = ('ffmpeg.exe', 
      '-y', 
      '-r', '%d' % rate, 
      '-f','image2pipe', 
      '-vcodec', 'mjpeg', 
      '-i', 'pipe:', 
      '-vcodec', 'libxvid', 
      outf 
      ) 
p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False) 

for i in range(10): 
    im = Image.fromarray(np.uint8(np.random.randn(100,100))) 
    p.stdin.write(im.tostring('jpeg','L')) 
    #p.communicate(im.tostring('jpeg','L')) 

p.stdin.close() 
11

Chuyển đổi sang định dạng hình ảnh khá chậm và thêm phụ thuộc. Sau khi nhìn vào các trang này và khác tôi đã nhận nó làm việc bằng cách sử dụng bộ đệm thô uncoded bằng cách sử dụng mencoder (ffmpeg giải pháp vẫn còn muốn).

chi tiết tại địa chỉ: http://vokicodder.blogspot.com/2011/02/numpy-arrays-to-video.html

import subprocess 

import numpy as np 

class VideoSink(object) : 

    def __init__(self, size, filename="output", rate=10, byteorder="bgra") : 
      self.size = size 
      cmdstring = ('mencoder', 
        '/dev/stdin', 
        '-demuxer', 'rawvideo', 
        '-rawvideo', 'w=%i:h=%i'%size[::-1]+":fps=%i:format=%s"%(rate,byteorder), 
        '-o', filename+'.avi', 
        '-ovc', 'lavc', 
        ) 
      self.p = subprocess.Popen(cmdstring, stdin=subprocess.PIPE, shell=False) 

    def run(self, image) : 
      assert image.shape == self.size 
      self.p.stdin.write(image.tostring()) 
    def close(self) : 
      self.p.stdin.close() 

Tôi có một số speedups tốt đẹp.

+0

Tôi đã sửa đổi điều này cho ffmpeg, xem câu trả lời của tôi bên dưới nếu bạn vẫn muốn – cxrodgers

4

Đây là tất cả câu trả lời thực sự tuyệt vời. Đây là một gợi ý khác. @ user621442 là chính xác rằng nút cổ chai thường là văn bản của hình ảnh, vì vậy nếu bạn đang viết tệp png cho máy nén video của bạn, nó sẽ được khá chậm (ngay cả khi bạn đang gửi chúng thông qua một đường ống thay vì ghi vào đĩa). Tôi tìm thấy một giải pháp sử dụng ffmpeg tinh khiết, mà cá nhân tôi thấy dễ sử dụng hơn matplotlib.animation hoặc mencoder.

Ngoài ra, trong trường hợp của mình, tôi muốn lưu hình ảnh vào trục, thay vì lưu tất cả các nhãn đánh dấu, tiêu đề hình, hình nền, v.v. Về cơ bản, tôi muốn tạo phim/hoạt ảnh bằng mã matplotlib , nhưng không có nó "trông giống như một đồ thị". Tôi đã bao gồm that code ở đây, nhưng bạn có thể tạo biểu đồ chuẩn và đặt chúng vào ffmpeg thay thế nếu bạn muốn.

import matplotlib.pyplot as plt 
import subprocess 

# create a figure window that is the exact size of the image 
# 400x500 pixels in my case 
# don't draw any axis stuff ... thanks to @Joe Kington for this trick 
# https://stackoverflow.com/questions/14908576/how-to-remove-frame-from-matplotlib-pyplot-figure-vs-matplotlib-figure-frame 
f = plt.figure(frameon=False, figsize=(4, 5), dpi=100) 
canvas_width, canvas_height = f.canvas.get_width_height() 
ax = f.add_axes([0, 0, 1, 1]) 
ax.axis('off') 

def update(frame): 
    # your matplotlib code goes here 

# 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(1000): 
    # draw the frame 
    update(frame) 
    plt.draw() 

    # extract the image as an ARGB string 
    string = f.canvas.tostring_argb() 

    # write to pipe 
    p.stdin.write(string) 

# Finish up 
p.communicate() 
Các vấn đề liên quan