[wip] Make config classes dumpable via YAML, rename base configs

pull/357/head
nyanpasu64 2018-07-24 04:28:53 -07:00
rodzic e1dfdd13da
commit 65655b2645
9 zmienionych plików z 94 dodań i 46 usunięć

20
ovgenpy/config.py 100644
Wyświetl plik

@ -0,0 +1,20 @@
from dataclasses import dataclass
from ruamel.yaml import YAML, yaml_object
# typing.NamedTuple is incompatible with yaml.register_class.
# dataclasses.dataclass is compatible.
# So use the latter.
# __init__-less classes are also compatible with yaml.register_class.
yaml = YAML()
def register_dataclass(cls):
# https://stackoverflow.com/a/51497219/2683842
# YAML.register_class(cls) has only returned cls since 2018-07-12.
return yaml_object(yaml)(
dataclass(cls)
)

Wyświetl plik

@ -4,7 +4,7 @@ import subprocess
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Type, List, Union from typing import TYPE_CHECKING, Type, List, Union
from dataclasses import dataclass from ovgenpy.config import register_dataclass
if TYPE_CHECKING: if TYPE_CHECKING:
import numpy as np import numpy as np
@ -14,7 +14,7 @@ if TYPE_CHECKING:
RGB_DEPTH = 3 RGB_DEPTH = 3
class OutputConfig: class IOutputConfig:
cls: 'Type[Output]' cls: 'Type[Output]'
def __call__(self, ovgen_cfg: 'Config'): def __call__(self, ovgen_cfg: 'Config'):
@ -22,7 +22,7 @@ class OutputConfig:
class Output(ABC): class Output(ABC):
def __init__(self, ovgen_cfg: 'Config', cfg: OutputConfig): def __init__(self, ovgen_cfg: 'Config', cfg: IOutputConfig):
self.ovgen_cfg = ovgen_cfg self.ovgen_cfg = ovgen_cfg
self.cfg = cfg self.cfg = cfg
@ -33,7 +33,7 @@ class Output(ABC):
# Glue logic # Glue logic
def register_output(config_t: Type[OutputConfig]): def register_output(config_t: Type[IOutputConfig]):
def inner(output_t: Type[Output]): def inner(output_t: Type[Output]):
config_t.cls = output_t config_t.cls = output_t
return output_t return output_t
@ -101,8 +101,8 @@ class ProcessOutput(Output):
# FFmpegOutput # FFmpegOutput
@dataclass @register_dataclass
class FFmpegOutputConfig(OutputConfig): class FFmpegOutputConfig(IOutputConfig):
path: str path: str
video_template: str = '-c:v libx264 -crf 18 -bf 2 -flags +cgop -pix_fmt yuv420p -movflags faststart' video_template: str = '-c:v libx264 -crf 18 -bf 2 -flags +cgop -pix_fmt yuv420p -movflags faststart'
audio_template: str = '-c:a aac -b:a 384k' audio_template: str = '-c:a aac -b:a 384k'
@ -121,7 +121,8 @@ class FFmpegOutput(ProcessOutput):
# FFplayOutput # FFplayOutput
class FFplayOutputConfig(OutputConfig): @register_dataclass
class FFplayOutputConfig(IOutputConfig):
video_template: str = '-c:v copy' video_template: str = '-c:v copy'
audio_template: str = '-c:a copy' audio_template: str = '-c:a copy'
@ -154,8 +155,8 @@ class FFplayOutput(ProcessOutput):
# ImageOutput # ImageOutput
@dataclass @register_dataclass
class ImageOutputConfig: class ImageOutputConfig(IOutputConfig):
path_prefix: str path_prefix: str

Wyświetl plik

@ -2,32 +2,34 @@
import time import time
from pathlib import Path from pathlib import Path
from typing import NamedTuple, Optional, List from typing import Optional, List
import click import click
from ovgenpy import outputs
from ovgenpy import outputs
from ovgenpy.config import register_dataclass
from ovgenpy.renderer import MatplotlibRenderer, RendererConfig from ovgenpy.renderer import MatplotlibRenderer, RendererConfig
from ovgenpy.triggers import TriggerConfig, CorrelationTrigger from ovgenpy.triggers import ITriggerConfig, CorrelationTriggerConfig
from ovgenpy.wave import WaveConfig, Wave from ovgenpy.wave import WaveConfig, Wave
RENDER_PROFILING = True RENDER_PROFILING = True
class Config(NamedTuple): @register_dataclass
class Config:
wave_dir: str wave_dir: str
audio_path: Optional[str] audio_path: Optional[str]
fps: int fps: int
time_visible_ms: int time_visible_ms: int
scan_ratio: float scan_ratio: float
trigger: TriggerConfig # Maybe overriden per Wave trigger: ITriggerConfig # Maybe overriden per Wave
amplification: float amplification: float
render: RendererConfig render: RendererConfig
outputs: List[outputs.OutputConfig] outputs: List[outputs.IOutputConfig]
create_window: bool create_window: bool
@property @property
@ -54,7 +56,7 @@ def main(wave_dir: str, audio_path: Optional[str], fps: int, output: str):
time_visible_ms=25, time_visible_ms=25,
scan_ratio=1, scan_ratio=1,
trigger=CorrelationTrigger.Config( trigger=CorrelationTriggerConfig(
trigger_strength=1, trigger_strength=1,
use_edge_trigger=False, use_edge_trigger=False,

Wyświetl plik

@ -2,23 +2,22 @@ from typing import Optional, List, Tuple, TYPE_CHECKING
import matplotlib import matplotlib
import numpy as np import numpy as np
from dataclasses import dataclass
from ovgenpy.config import register_dataclass
from ovgenpy.outputs import RGB_DEPTH
from ovgenpy.util import ceildiv
matplotlib.use('agg') 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 RGB_DEPTH
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
from matplotlib.lines import Line2D from matplotlib.lines import Line2D
@register_dataclass
@dataclass
class RendererConfig: class RendererConfig:
width: int width: int
height: int height: int

Wyświetl plik

@ -2,9 +2,9 @@ from abc import ABC, abstractmethod
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import numpy as np import numpy as np
from dataclasses import dataclass
from scipy import signal from scipy import signal
from ovgenpy.config import register_dataclass
from ovgenpy.util import find from ovgenpy.util import find
from ovgenpy.wave import FLOAT from ovgenpy.wave import FLOAT
@ -27,12 +27,11 @@ class Trigger(ABC):
... ...
class TriggerConfig: class ITriggerConfig:
# NamedTuple inheritance does not work. Mark children @dataclass instead.
# https://github.com/python/typing/issues/427
def __call__(self, wave: 'Wave', scan_nsamp: int): def __call__(self, wave: 'Wave', scan_nsamp: int):
# idea: __call__ return self.cls(wave, scan_nsamp, cfg=self) # idea: __call__ return self.cls(wave, scan_nsamp, cfg=self)
# problem: cannot reference XTrigger from within XTrigger # problem: cannot reference XTrigger from within XTrigger
# solution: @register_trigger(XTriggerCfg)
raise NotImplementedError raise NotImplementedError
@ -40,11 +39,8 @@ def lerp(x: np.ndarray, y: np.ndarray, a: float):
return x * (1 - a) + y * a return x * (1 - a) + y * a
class CorrelationTrigger(Trigger): @register_dataclass
MIN_AMPLITUDE = 0.01 class CorrelationTriggerConfig(ITriggerConfig):
@dataclass
class Config(TriggerConfig):
# get_trigger # get_trigger
trigger_strength: float trigger_strength: float
use_edge_trigger: bool use_edge_trigger: bool
@ -56,10 +52,13 @@ class CorrelationTrigger(Trigger):
def __call__(self, wave: 'Wave', scan_nsamp: int): def __call__(self, wave: 'Wave', scan_nsamp: int):
return CorrelationTrigger(wave, scan_nsamp, cfg=self) return CorrelationTrigger(wave, scan_nsamp, cfg=self)
class CorrelationTrigger(Trigger):
MIN_AMPLITUDE = 0.01
# get_trigger postprocessing: self._zero_trigger # get_trigger postprocessing: self._zero_trigger
ZERO_CROSSING_SCAN = 256 ZERO_CROSSING_SCAN = 256
def __init__(self, wave: 'Wave', scan_nsamp: int, cfg: Config): def __init__(self, wave: 'Wave', scan_nsamp: int, cfg: CorrelationTriggerConfig):
""" """
Correlation-based trigger which looks at a window of `scan_nsamp` samples. Correlation-based trigger which looks at a window of `scan_nsamp` samples.

Wyświetl plik

@ -1,13 +1,16 @@
from typing import NamedTuple, TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
import numpy as np import numpy as np
from scipy.io import wavfile from scipy.io import wavfile
from ovgenpy.config import register_dataclass
if TYPE_CHECKING: if TYPE_CHECKING:
from ovgenpy.triggers import Trigger from ovgenpy.triggers import Trigger
class WaveConfig(NamedTuple): @register_dataclass
class WaveConfig:
amplification: float = 1 amplification: float = 1

Wyświetl plik

@ -10,5 +10,5 @@ setup(
author_email='', author_email='',
description='', description='',
install_requires=['numpy', 'scipy', 'imageio', 'click', 'matplotlib', install_requires=['numpy', 'scipy', 'imageio', 'click', 'matplotlib',
'dataclasses;python_version<"3.7"'] 'dataclasses;python_version<"3.7"', 'ruamel.yaml']
) )

Wyświetl plik

@ -0,0 +1,24 @@
import sys
from ruamel.yaml import yaml_object
from ovgenpy.config import register_dataclass, yaml
def test_register_dataclass():
@register_dataclass
class Foo:
foo: int
bar: int
yaml.dump(Foo(1, 2), sys.stdout)
print()
def test_yaml_object():
@yaml_object(yaml)
class Bar:
pass
yaml.dump(Bar(), sys.stdout)
print()

Wyświetl plik

@ -4,7 +4,7 @@ from matplotlib.axes import Axes
from matplotlib.figure import Figure from matplotlib.figure import Figure
from ovgenpy import triggers from ovgenpy import triggers
from ovgenpy.triggers import CorrelationTrigger from ovgenpy.triggers import CorrelationTriggerConfig
from ovgenpy.wave import Wave from ovgenpy.wave import Wave
@ -14,7 +14,7 @@ triggers.SHOW_TRIGGER = False
@pytest.fixture(scope='session', params=[False, True]) @pytest.fixture(scope='session', params=[False, True])
def cfg(request): def cfg(request):
use_edge_trigger = request.param use_edge_trigger = request.param
return CorrelationTrigger.Config( return CorrelationTriggerConfig(
trigger_strength=1, trigger_strength=1,
use_edge_trigger=use_edge_trigger, use_edge_trigger=use_edge_trigger,