kopia lustrzana https://github.com/corrscope/corrscope
Render to video using raw image data. Hide GUI.
Hide GUI since it changes window size, breaking the rendering process.pull/357/head
rodzic
9816001f9d
commit
b14d60fd75
|
@ -2,16 +2,16 @@
|
|||
import shlex
|
||||
import subprocess
|
||||
from abc import ABC, abstractmethod
|
||||
from io import BytesIO
|
||||
from typing import Optional, TYPE_CHECKING, Type, Callable, TypeVar, List
|
||||
from typing import TYPE_CHECKING, Type, List
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy as np
|
||||
from ovgenpy.ovgenpy import Config
|
||||
|
||||
|
||||
IMAGE_FORMAT = 'png'
|
||||
RGB_DEPTH = 3
|
||||
|
||||
|
||||
class OutputConfig:
|
||||
|
@ -27,9 +27,8 @@ class Output(ABC):
|
|||
self.cfg = cfg
|
||||
|
||||
@abstractmethod
|
||||
def output_frame_png(self, frame: bytes) -> None:
|
||||
""" Output an encoded PNG file. TODO PNG compression overhead is bad """
|
||||
pass
|
||||
def write_frame(self, frame: 'np.ndarray') -> None:
|
||||
""" Output a Numpy ndarray. """
|
||||
|
||||
|
||||
# Glue logic
|
||||
|
@ -49,9 +48,15 @@ FFMPEG = 'ffmpeg'
|
|||
FFPLAY = 'ffplay'
|
||||
|
||||
|
||||
def ffmpeg_input_video(fps: int) -> List[str]:
|
||||
# Removed: '-c:v {IMAGE_FORMAT}' since it doesn't work
|
||||
return ['-f image2pipe -framerate', str(fps), '-i -']
|
||||
assert RGB_DEPTH == 3
|
||||
def ffmpeg_input_video(cfg: 'Config') -> List[str]:
|
||||
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]:
|
||||
|
@ -93,7 +98,7 @@ class FFmpegOutput(Output):
|
|||
templates: List[str] = [FFMPEG]
|
||||
|
||||
# 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:
|
||||
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.
|
||||
# https://stackoverflow.com/a/9886747
|
||||
|
||||
def output_frame_png(self, frame: bytes) -> None:
|
||||
def write_frame(self, frame: bytes) -> None:
|
||||
self._stream.write(frame)
|
||||
|
||||
def close(self):
|
||||
|
|
|
@ -65,16 +65,13 @@ def main(wave_dir: str, audio_path: Optional[str], fps: int, output: str):
|
|||
outputs=[
|
||||
outputs.FFmpegOutputConfig(output)
|
||||
],
|
||||
create_window=True
|
||||
create_window=False
|
||||
)
|
||||
|
||||
ovgen = Ovgen(cfg)
|
||||
ovgen.write()
|
||||
|
||||
|
||||
COLOR_CHANNELS = 3
|
||||
|
||||
|
||||
class Ovgen:
|
||||
def __init__(self, cfg: Config):
|
||||
self.cfg = cfg
|
||||
|
@ -148,6 +145,8 @@ class Ovgen:
|
|||
|
||||
# Output frame
|
||||
frame = renderer.get_frame()
|
||||
for output in self.outputs:
|
||||
output.write_frame(frame)
|
||||
|
||||
# TODO write to file
|
||||
# how to write ndarray to ffmpeg?
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
from typing import Optional, List, Tuple, TYPE_CHECKING
|
||||
|
||||
import matplotlib
|
||||
import numpy as np
|
||||
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.backends.backend_agg import FigureCanvasAgg
|
||||
|
||||
from ovgenpy.outputs import IMAGE_FORMAT
|
||||
from ovgenpy.util import ceildiv
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.figure import Figure
|
||||
|
@ -167,10 +170,8 @@ class MatplotlibRenderer:
|
|||
self.fig.canvas.draw()
|
||||
self.fig.canvas.flush_events()
|
||||
|
||||
assert IMAGE_FORMAT == 'png'
|
||||
RGB_DEPTH = 3
|
||||
|
||||
def get_frame(self):
|
||||
def get_frame(self) -> np.ndarray:
|
||||
""" Returns ndarray of shape w,h,3. """
|
||||
canvas = self.fig.canvas
|
||||
|
||||
# Agg is the default noninteractive backend except on OSX.
|
||||
|
@ -179,12 +180,13 @@ class MatplotlibRenderer:
|
|||
raise RuntimeError(
|
||||
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.frombuffer(canvas.tostring_rgb(), np.uint8)
|
||||
print(buffer_rgb.shape)
|
||||
np.reshape(buffer_rgb, (w, h, self.RGB_DEPTH))
|
||||
buffer_rgb: np.ndarray = np.frombuffer(canvas.tostring_rgb(), np.uint8) # TODO Pycharm type inference error
|
||||
np.reshape(buffer_rgb, (w, h, RGB_DEPTH))
|
||||
assert buffer_rgb.size == w * h * RGB_DEPTH
|
||||
|
||||
return buffer_rgb
|
||||
# # TODO https://matplotlib.org/api/_as_gen/matplotlib.pyplot.imsave.html to
|
||||
|
|
Ładowanie…
Reference in New Issue