kopia lustrzana https://github.com/corrscope/corrscope
[wip] Make config classes dumpable via YAML, rename base configs
rodzic
e1dfdd13da
commit
65655b2645
|
@ -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)
|
||||||
|
)
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -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']
|
||||||
)
|
)
|
||||||
|
|
|
@ -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()
|
|
@ -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,
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue