LoginSignup
2
0

More than 5 years have passed since last update.

pyOpenGL + ffmpeg.exeでmovie fileを作る。

Last updated at Posted at 2017-08-25

非力なGPUで重いshaderは、ちょっと厳しい。そんな時は、movie fileを作るが良いかな。
twitterとかにも投稿できるし良いかな。

とりあえずffmpeg.exeを手に入れてパスを通してください。

変数の説明

画面サイズ(幅) : XRES = 640 
画面サイズ(高さ) : YRES = 480
連番jpg fileの保存フォルダ : tempDirName = "hogehoge"
製作movie fileの名前 : movieFileName = "video.mp4"
映像描写時間(秒) : duration = 10
フレームレート : fps = 30


from OpenGL.GL import *
from OpenGL.WGL import *
from ctypes import *
from ctypes.wintypes import *
from PIL import Image
import numpy
import sys
import os
import subprocess

kernel32 = windll.kernel32
user32 = windll.user32
winmm = windll.winmm 

XRES = 640
YRES = 480
tempDirName = "hogehoge"
movieFileName = "video.mp4"
duration = 10
fps = 30

vsh = """
#version 430

layout(location = 0) in vec2 p;

void main(){
    gl_Position = vec4(p, 0, 1);
}
"""

fsh = """
#version 430

out vec4 fragColor;

uniform vec2 resolution;
uniform float time;

void main() {
    vec2 p = (2.0 * gl_FragCoord.xy - resolution.xy) / resolution.y;
    p += vec2(cos(time), sin(time)) * 0.5;
    float g = exp(-1.5 * dot(p,p));
    fragColor = vec4(g, g, g, 1);
}
"""

# window init
WS_OVERLAPPEDWINDOW = 0xcf0000
hWnd = user32.CreateWindowExA(0,0xC018,0,WS_OVERLAPPEDWINDOW,0,0,XRES,YRES,0,0,0,0)
hdc = user32.GetDC(hWnd)   
user32.SetForegroundWindow(hWnd)

# GL context init
PFD_SUPPORT_OPENGL = 32
PFD_DOUBLEBUFFER = 1
pfd = PIXELFORMATDESCRIPTOR(0,1,PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER,32,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0)
SetPixelFormat(hdc, ChoosePixelFormat(hdc, pfd), pfd)
hGLrc = wglCreateContext(hdc)
wglMakeCurrent(hdc, hGLrc)

# GL init
glClearColor(0, 0, 0, 1)
glEnable(GL_CULL_FACE)
glCullFace(GL_BACK)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)

program = glCreateProgram()
for s, t in zip((vsh, fsh), (GL_VERTEX_SHADER, GL_FRAGMENT_SHADER)):    
    shader = glCreateShader(t)
    glShaderSource(shader, s)
    glCompileShader(shader)
    if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
        raise RuntimeError(glGetShaderInfoLog(shader).decode())
    glAttachShader(program, shader)
glLinkProgram(program)
glUseProgram(program)
glUniform2f(glGetUniformLocation(program, "resolution"), XRES, YRES)
vertices = numpy.array([-1,-1,1,-1,-1,1,1,1], numpy.float32)
glBindBuffer(GL_ARRAY_BUFFER, glGenBuffers(1))
glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, (c_float*len(vertices))(*vertices), GL_STATIC_DRAW)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)

# make jpg files
pixels = (GLuint* (3 * XRES * YRES))()

if not os.path.exists(tempDirName): os.mkdir(tempDirName)

for i in range(duration * fps):
    t= i/fps
    filename = "{0}/img{1:05d}.jpg".format(tempDirName, i+1)
    sys.stdout.write("\r make jpg file: {0} TIME : {1:.2f}".format(filename,t))
    sys.stdout.flush()
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glUniform1f(glGetUniformLocation(program, "time"), t)
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
    glReadPixels(0, 0, XRES, YRES, GL_RGB, GL_UNSIGNED_BYTE, pixels)
    image = Image.frombytes(mode="RGB", size=(XRES, YRES), data=pixels)     
    image = image.transpose(Image.FLIP_TOP_BOTTOM)
    image.save(filename)

# make mp4 file
subprocess.call("ffmpeg -f image2 -r {1:d} -i {0}/img%05d.jpg -r {1:d} -an -vcodec libx264 -pix_fmt yuv420p {2} -y".format(tempDirName,fps,movieFileName))

# GL context finish
wglMakeCurrent(0, 0)
wglDeleteContext(hGLrc)

# windoe finish
user32.ReleaseDC(hWnd, hdc)
user32.PostQuitMessage(0)
user32.DestroyWindow(hWnd)

終わりに

全部を説明すると大変なことになるので、ソースしかのせてません。
興味ある方は、fragment shaderを書き直してpythonで使ってみてください。
ffmpeg.exeを使って音楽とか入れてみても面白いと思います。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0