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 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):
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
Ładowanie…
Reference in New Issue