holobench 1.25.2__py3-none-any.whl → 1.27.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bencher/bench_report.py +6 -109
- bencher/example/__init__.py +0 -0
- bencher/example/benchmark_data.py +196 -0
- bencher/example/example_all.py +45 -0
- bencher/example/example_categorical.py +99 -0
- bencher/example/example_composable_container.py +106 -0
- bencher/example/example_composable_container2.py +160 -0
- bencher/example/example_consts.py +39 -0
- bencher/example/example_custom_sweep.py +59 -0
- bencher/example/example_custom_sweep2.py +42 -0
- bencher/example/example_docs.py +34 -0
- bencher/example/example_filepath.py +27 -0
- bencher/example/example_float3D.py +101 -0
- bencher/example/example_float_cat.py +99 -0
- bencher/example/example_floats.py +89 -0
- bencher/example/example_floats2D.py +93 -0
- bencher/example/example_holosweep.py +98 -0
- bencher/example/example_holosweep_objects.py +111 -0
- bencher/example/example_holosweep_tap.py +144 -0
- bencher/example/example_image.py +155 -0
- bencher/example/example_levels.py +181 -0
- bencher/example/example_levels2.py +37 -0
- bencher/example/example_pareto.py +53 -0
- bencher/example/example_sample_cache.py +85 -0
- bencher/example/example_sample_cache_context.py +116 -0
- bencher/example/example_simple.py +134 -0
- bencher/example/example_simple_bool.py +35 -0
- bencher/example/example_simple_cat.py +48 -0
- bencher/example/example_simple_float.py +28 -0
- bencher/example/example_simple_float2d.py +29 -0
- bencher/example/example_strings.py +47 -0
- bencher/example/example_time_event.py +63 -0
- bencher/example/example_video.py +118 -0
- bencher/example/example_workflow.py +189 -0
- bencher/example/experimental/example_bokeh_plotly.py +38 -0
- bencher/example/experimental/example_hover_ex.py +45 -0
- bencher/example/experimental/example_hvplot_explorer.py +39 -0
- bencher/example/experimental/example_interactive.py +75 -0
- bencher/example/experimental/example_streamnd.py +49 -0
- bencher/example/experimental/example_streams.py +36 -0
- bencher/example/experimental/example_template.py +40 -0
- bencher/example/experimental/example_updates.py +84 -0
- bencher/example/experimental/example_vector.py +84 -0
- bencher/example/meta/example_meta.py +171 -0
- bencher/example/meta/example_meta_cat.py +25 -0
- bencher/example/meta/example_meta_float.py +23 -0
- bencher/example/meta/example_meta_levels.py +26 -0
- bencher/example/optuna/example_optuna.py +78 -0
- bencher/example/shelved/example_float2D_scatter.py +109 -0
- bencher/example/shelved/example_float3D_cone.py +96 -0
- bencher/example/shelved/example_kwargs.py +63 -0
- bencher/plotting/__init__.py +0 -0
- bencher/plotting/plot_filter.py +110 -0
- bencher/plotting/plt_cnt_cfg.py +75 -0
- bencher/results/__init__.py +0 -0
- bencher/results/bench_result.py +94 -0
- bencher/results/bench_result_base.py +476 -0
- bencher/results/composable_container/__init__.py +0 -0
- bencher/results/composable_container/composable_container_base.py +73 -0
- bencher/results/composable_container/composable_container_panel.py +39 -0
- bencher/results/composable_container/composable_container_video.py +184 -0
- bencher/results/float_formatter.py +44 -0
- bencher/results/holoview_result.py +753 -0
- bencher/results/optuna_result.py +354 -0
- bencher/results/panel_result.py +41 -0
- bencher/results/plotly_result.py +65 -0
- bencher/results/video_result.py +38 -0
- bencher/results/video_summary.py +222 -0
- bencher/variables/__init__.py +0 -0
- bencher/variables/inputs.py +202 -0
- bencher/variables/parametrised_sweep.py +208 -0
- bencher/variables/results.py +214 -0
- bencher/variables/sweep_base.py +162 -0
- bencher/variables/time.py +92 -0
- holobench-1.27.0.data/data/share/ament_index/resource_index/packages/bencher +0 -0
- holobench-1.27.0.data/data/share/bencher/package.xml +33 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/METADATA +5 -5
- holobench-1.27.0.dist-info/RECORD +93 -0
- holobench-1.25.2.dist-info/RECORD +0 -18
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/LICENSE +0 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/WHEEL +0 -0
- {holobench-1.25.2.dist-info → holobench-1.27.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,184 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import numpy as np
|
3
|
+
from copy import deepcopy
|
4
|
+
from pathlib import Path
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from moviepy.editor import (
|
7
|
+
ImageClip,
|
8
|
+
CompositeVideoClip,
|
9
|
+
clips_array,
|
10
|
+
concatenate_videoclips,
|
11
|
+
VideoClip,
|
12
|
+
VideoFileClip,
|
13
|
+
)
|
14
|
+
from moviepy.video.fx.margin import margin
|
15
|
+
|
16
|
+
from bencher.results.composable_container.composable_container_base import (
|
17
|
+
ComposableContainerBase,
|
18
|
+
ComposeType,
|
19
|
+
)
|
20
|
+
from bencher.video_writer import VideoWriter
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass()
|
24
|
+
class RenderCfg:
|
25
|
+
compose_method: ComposeType = ComposeType.sequence
|
26
|
+
var_name: str = None
|
27
|
+
var_value: str = None
|
28
|
+
background_col: tuple[int, int, int] = (255, 255, 255)
|
29
|
+
duration: float = 10.0
|
30
|
+
duration_target: bool = True
|
31
|
+
min_frame_duration: float = 1.0 / 30
|
32
|
+
max_frame_duration: float = 2.0
|
33
|
+
margin: int = 0
|
34
|
+
|
35
|
+
|
36
|
+
@dataclass
|
37
|
+
class ComposableContainerVideo(ComposableContainerBase):
|
38
|
+
def append(self, obj: VideoClip | ImageClip | str | np.ndarray) -> None:
|
39
|
+
"""Appends an image or video to the container
|
40
|
+
|
41
|
+
Args:
|
42
|
+
obj (VideoClip | ImageClip | str | np.ndarray): Any representation of an image or video
|
43
|
+
|
44
|
+
Raises:
|
45
|
+
RuntimeWarning: if file format is not recognised
|
46
|
+
"""
|
47
|
+
|
48
|
+
# print(f"append obj: {type(obj)}, {obj}")
|
49
|
+
if obj is not None:
|
50
|
+
if isinstance(obj, VideoClip):
|
51
|
+
self.container.append(obj)
|
52
|
+
elif isinstance(obj, ComposableContainerVideo):
|
53
|
+
self.container.append(obj.render())
|
54
|
+
elif isinstance(obj, np.ndarray):
|
55
|
+
self.container.append(ImageClip(obj))
|
56
|
+
else:
|
57
|
+
path = Path(obj)
|
58
|
+
extension = str.lower(path.suffix)
|
59
|
+
if extension in [".jpg", ".jepg", ".png"]:
|
60
|
+
self.container.append(ImageClip(obj))
|
61
|
+
elif extension in [".mpeg", ".mpg", ".mp4", ".webm"]:
|
62
|
+
# print(obj)
|
63
|
+
self.container.append(VideoFileClip(obj))
|
64
|
+
else:
|
65
|
+
raise RuntimeWarning(f"unsupported filetype {extension}")
|
66
|
+
else:
|
67
|
+
raise RuntimeWarning("No data passed to ComposableContainerVideo.append()")
|
68
|
+
|
69
|
+
def calculate_duration(self, frames, render_cfg: RenderCfg):
|
70
|
+
if render_cfg.duration_target:
|
71
|
+
# calculate duration based on fps constraints
|
72
|
+
duration = 10.0 if render_cfg.duration is None else render_cfg.duration
|
73
|
+
frame_duration = duration / frames
|
74
|
+
if render_cfg.min_frame_duration is not None:
|
75
|
+
frame_duration = max(frame_duration, render_cfg.min_frame_duration)
|
76
|
+
if render_cfg.max_frame_duration is not None:
|
77
|
+
frame_duration = min(frame_duration, render_cfg.max_frame_duration)
|
78
|
+
duration = frame_duration * frames
|
79
|
+
else:
|
80
|
+
duration = render_cfg.duration
|
81
|
+
frame_duration = duration / float(frames)
|
82
|
+
|
83
|
+
print("max_frame_duration", render_cfg.max_frame_duration)
|
84
|
+
print("DURATION", duration)
|
85
|
+
|
86
|
+
return duration, frame_duration
|
87
|
+
|
88
|
+
def render(self, render_cfg: RenderCfg = None, **kwargs) -> CompositeVideoClip:
|
89
|
+
"""Composes the images/videos into a single image/video based on the type of compose method
|
90
|
+
|
91
|
+
Args:
|
92
|
+
compose_method (ComposeType, optional): optionally override the default compose type. Defaults to None.
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
CompositeVideoClip: A composite video clip containing the images/videos added via append()
|
96
|
+
"""
|
97
|
+
if render_cfg is None:
|
98
|
+
render_cfg = RenderCfg(**kwargs)
|
99
|
+
|
100
|
+
print("rc", render_cfg)
|
101
|
+
_, frame_duration = self.calculate_duration(float(len(self.container)), render_cfg)
|
102
|
+
out = None
|
103
|
+
print(f"using compose type{render_cfg.compose_method}")
|
104
|
+
max_duration = 0.0
|
105
|
+
|
106
|
+
for i in range(len(self.container)):
|
107
|
+
if self.container[i].duration is None:
|
108
|
+
self.container[i].duration = frame_duration
|
109
|
+
max_duration = max(max_duration, self.container[i].duration)
|
110
|
+
match render_cfg.compose_method:
|
111
|
+
case ComposeType.right | ComposeType.down:
|
112
|
+
for i in range(len(self.container)):
|
113
|
+
self.container[i] = self.extend_clip(self.container[i], max_duration)
|
114
|
+
self.container[i] = margin(
|
115
|
+
self.container[i], top=render_cfg.margin, color=render_cfg.background_col
|
116
|
+
)
|
117
|
+
|
118
|
+
if render_cfg.compose_method == ComposeType.right:
|
119
|
+
clips = [self.container]
|
120
|
+
else:
|
121
|
+
clips = [[c] for c in self.container]
|
122
|
+
out = clips_array(clips, bg_color=render_cfg.background_col)
|
123
|
+
if out.duration is None:
|
124
|
+
out.duration = max_duration
|
125
|
+
case ComposeType.sequence:
|
126
|
+
out = concatenate_videoclips(
|
127
|
+
self.container, bg_color=render_cfg.background_col, method="compose"
|
128
|
+
)
|
129
|
+
# case ComposeType.overlay:
|
130
|
+
# for i in range(len(self.container)):
|
131
|
+
# self.container[i].alpha = 1./len(self.container)
|
132
|
+
# out = CompositeVideoClip(self.container, bg_color=render_args.background_col)
|
133
|
+
# out.duration = fps
|
134
|
+
case _:
|
135
|
+
raise RuntimeError("This compose type is not supported")
|
136
|
+
|
137
|
+
label = self.label_formatter(render_cfg.var_name, render_cfg.var_value)
|
138
|
+
if label is not None:
|
139
|
+
# print("adding label")
|
140
|
+
label = ImageClip(
|
141
|
+
np.array(VideoWriter.create_label(label, color=render_cfg.background_col))
|
142
|
+
)
|
143
|
+
label.duration = out.duration
|
144
|
+
label_compose = ComposeType.down
|
145
|
+
if render_cfg.compose_method == ComposeType.down:
|
146
|
+
label_compose = ComposeType.right
|
147
|
+
con2 = ComposableContainerVideo()
|
148
|
+
con2.append(label)
|
149
|
+
con2.append(out)
|
150
|
+
return con2.render(
|
151
|
+
RenderCfg(
|
152
|
+
background_col=render_cfg.background_col,
|
153
|
+
compose_method=label_compose,
|
154
|
+
duration=out.duration,
|
155
|
+
duration_target=False, # want exact duration
|
156
|
+
)
|
157
|
+
)
|
158
|
+
return out
|
159
|
+
|
160
|
+
def to_video(
|
161
|
+
self,
|
162
|
+
render_args: RenderCfg = None,
|
163
|
+
) -> str:
|
164
|
+
"""Returns the composite video clip as a webm file path
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
str: webm filepath
|
168
|
+
"""
|
169
|
+
return VideoWriter().write_video_raw(self.render(render_args))
|
170
|
+
|
171
|
+
def deep(self):
|
172
|
+
return deepcopy(self)
|
173
|
+
|
174
|
+
def extend_clip(self, clip: VideoClip, desired_duration: float):
|
175
|
+
if clip.duration < desired_duration:
|
176
|
+
return concatenate_videoclips(
|
177
|
+
[
|
178
|
+
clip,
|
179
|
+
ImageClip(
|
180
|
+
clip.get_frame(clip.duration), duration=desired_duration - clip.duration
|
181
|
+
),
|
182
|
+
]
|
183
|
+
)
|
184
|
+
return clip
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# from https://stackoverflow.com/questions/22989372/how-to-format-a-floating-number-to-maximum-fixed-width-in-python
|
2
|
+
|
3
|
+
|
4
|
+
class FormatFloat:
|
5
|
+
def __init__(self, width=8):
|
6
|
+
self.width = width
|
7
|
+
self.maxnum = int("9" * (width - 1)) # 9999999
|
8
|
+
self.minnum = -int("9" * (width - 2)) # -999999
|
9
|
+
|
10
|
+
def __call__(self, x):
|
11
|
+
# for small numbers
|
12
|
+
# if -999,999 < given < 9,999,999:
|
13
|
+
if self.minnum < x < self.maxnum:
|
14
|
+
# o = f'{x:7}'
|
15
|
+
o = f"{x:{self.width - 1}}"
|
16
|
+
|
17
|
+
# converting int to float without adding zero
|
18
|
+
if "." not in o:
|
19
|
+
o += "."
|
20
|
+
|
21
|
+
# float longer than 8 will need rounding to fit width
|
22
|
+
elif len(o) > self.width:
|
23
|
+
# output = str(round(x, 7 - str(x).index(".")))
|
24
|
+
o = str(round(x, self.width - 1 - str(x).index(".")))
|
25
|
+
if len(o) < self.width:
|
26
|
+
o += (self.width - len(o)) * "0"
|
27
|
+
|
28
|
+
else:
|
29
|
+
# for exponents
|
30
|
+
# added a loop for super large numbers or negative as "-" is another char
|
31
|
+
# Added max(max_char, 5) to account for max length of less
|
32
|
+
# than 5, was having too much fun
|
33
|
+
# TODO can i come up with a threshold value for these up front,
|
34
|
+
# so that i dont have to do this calc for every value??
|
35
|
+
for n in range(max(self.width, 5) - 5, 0, -1):
|
36
|
+
fill = f".{n}e"
|
37
|
+
o = f"{x:{fill}}".replace("+0", "+")
|
38
|
+
|
39
|
+
# if all good stop looping
|
40
|
+
if len(o) == self.width:
|
41
|
+
break
|
42
|
+
else:
|
43
|
+
raise ValueError(f"Number is too large to fit in {self.width} characters", x)
|
44
|
+
return o
|