Render to video using raw image data. Hide GUI.

Hide GUI since it changes window size, breaking the rendering process.
pull/357/head
nyanpasu64 2018-07-22 03:17:31 -07:00
rodzic 9816001f9d
commit b14d60fd75
3 zmienionych plików z 33 dodań i 27 usunięć

Wyświetl plik

@ -2,16 +2,16 @@
import shlex import shlex
import subprocess import subprocess
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from io import BytesIO from typing import TYPE_CHECKING, Type, List
from typing import Optional, TYPE_CHECKING, Type, Callable, TypeVar, List
from dataclasses import dataclass from dataclasses import dataclass
if TYPE_CHECKING: if TYPE_CHECKING:
import numpy as np
from ovgenpy.ovgenpy import Config from ovgenpy.ovgenpy import Config
IMAGE_FORMAT = 'png' RGB_DEPTH = 3
class OutputConfig: class OutputConfig:
@ -27,9 +27,8 @@ class Output(ABC):
self.cfg = cfg self.cfg = cfg
@abstractmethod @abstractmethod
def output_frame_png(self, frame: bytes) -> None: def write_frame(self, frame: 'np.ndarray') -> None:
""" Output an encoded PNG file. TODO PNG compression overhead is bad """ """ Output a Numpy ndarray. """
pass
# Glue logic # Glue logic
@ -49,9 +48,15 @@ FFMPEG = 'ffmpeg'
FFPLAY = 'ffplay' FFPLAY = 'ffplay'
def ffmpeg_input_video(fps: int) -> List[str]: assert RGB_DEPTH == 3
# Removed: '-c:v {IMAGE_FORMAT}' since it doesn't work def ffmpeg_input_video(cfg: 'Config') -> List[str]:
return ['-f image2pipe -framerate', str(fps), '-i -'] fps = cfg.fps
width = cfg.render.width
height = cfg.render.height
return [f'-f rawvideo -pixel_format rgb24 -video_size {width}x{height}',
f'-framerate {fps}',
'-i -']
def ffmpeg_input_audio(audio_path: str) -> List[str]: def ffmpeg_input_audio(audio_path: str) -> List[str]:
@ -93,7 +98,7 @@ class FFmpegOutput(Output):
templates: List[str] = [FFMPEG] templates: List[str] = [FFMPEG]
# TODO factor out "get_ffmpeg_input"... what if wrong abstraction? # TODO factor out "get_ffmpeg_input"... what if wrong abstraction?
templates += ffmpeg_input_video(fps=ovgen_cfg.fps) # video templates += ffmpeg_input_video(ovgen_cfg) # video
if ovgen_cfg.audio_path: if ovgen_cfg.audio_path:
templates += ffmpeg_input_audio(audio_path=ovgen_cfg.audio_path) # audio templates += ffmpeg_input_audio(audio_path=ovgen_cfg.audio_path) # audio
@ -113,7 +118,7 @@ class FFmpegOutput(Output):
# Python documentation discourages accessing popen.stdin. It's wrong. # Python documentation discourages accessing popen.stdin. It's wrong.
# https://stackoverflow.com/a/9886747 # https://stackoverflow.com/a/9886747
def output_frame_png(self, frame: bytes) -> None: def write_frame(self, frame: bytes) -> None:
self._stream.write(frame) self._stream.write(frame)
def close(self): def close(self):

Wyświetl plik

@ -65,16 +65,13 @@ def main(wave_dir: str, audio_path: Optional[str], fps: int, output: str):
outputs=[ outputs=[
outputs.FFmpegOutputConfig(output) outputs.FFmpegOutputConfig(output)
], ],
create_window=True create_window=False
) )
ovgen = Ovgen(cfg) ovgen = Ovgen(cfg)
ovgen.write() ovgen.write()
COLOR_CHANNELS = 3
class Ovgen: class Ovgen:
def __init__(self, cfg: Config): def __init__(self, cfg: Config):
self.cfg = cfg self.cfg = cfg
@ -148,6 +145,8 @@ class Ovgen:
# Output frame # Output frame
frame = renderer.get_frame() frame = renderer.get_frame()
for output in self.outputs:
output.write_frame(frame)
# TODO write to file # TODO write to file
# how to write ndarray to ffmpeg? # how to write ndarray to ffmpeg?

Wyświetl plik

@ -1,13 +1,16 @@
from typing import Optional, List, Tuple, TYPE_CHECKING from typing import Optional, List, Tuple, TYPE_CHECKING
import matplotlib
import numpy as np import numpy as np
from dataclasses import dataclass from dataclasses import dataclass
from ovgenpy.outputs import RGB_DEPTH
from ovgenpy.util import ceildiv
matplotlib.use('agg')
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg from matplotlib.backends.backend_agg import FigureCanvasAgg
from ovgenpy.outputs import IMAGE_FORMAT
from ovgenpy.util import ceildiv
if TYPE_CHECKING: if TYPE_CHECKING:
from matplotlib.axes import Axes from matplotlib.axes import Axes
from matplotlib.figure import Figure from matplotlib.figure import Figure
@ -167,10 +170,8 @@ class MatplotlibRenderer:
self.fig.canvas.draw() self.fig.canvas.draw()
self.fig.canvas.flush_events() self.fig.canvas.flush_events()
assert IMAGE_FORMAT == 'png' def get_frame(self) -> np.ndarray:
RGB_DEPTH = 3 """ Returns ndarray of shape w,h,3. """
def get_frame(self):
canvas = self.fig.canvas canvas = self.fig.canvas
# Agg is the default noninteractive backend except on OSX. # Agg is the default noninteractive backend except on OSX.
@ -179,12 +180,13 @@ class MatplotlibRenderer:
raise RuntimeError( raise RuntimeError(
f'oh shit, cannot read data from {type(canvas)} != FigureCanvasAgg') f'oh shit, cannot read data from {type(canvas)} != FigureCanvasAgg')
# buffer_rgba, (w, h) = canvas.print_to_buffer() w = self.cfg.width
h = self.cfg.height
assert (w, h) == canvas.get_width_height()
w, h = canvas.get_width_height() buffer_rgb: np.ndarray = np.frombuffer(canvas.tostring_rgb(), np.uint8) # TODO Pycharm type inference error
buffer_rgb = np.frombuffer(canvas.tostring_rgb(), np.uint8) np.reshape(buffer_rgb, (w, h, RGB_DEPTH))
print(buffer_rgb.shape) assert buffer_rgb.size == w * h * RGB_DEPTH
np.reshape(buffer_rgb, (w, h, self.RGB_DEPTH))
return buffer_rgb return buffer_rgb
# # TODO https://matplotlib.org/api/_as_gen/matplotlib.pyplot.imsave.html to # # TODO https://matplotlib.org/api/_as_gen/matplotlib.pyplot.imsave.html to