holobench 1.25.2__py3-none-any.whl → 1.26.3__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.
Files changed (81) hide show
  1. bencher/example/__init__.py +0 -0
  2. bencher/example/benchmark_data.py +196 -0
  3. bencher/example/example_all.py +45 -0
  4. bencher/example/example_categorical.py +99 -0
  5. bencher/example/example_composable_container.py +106 -0
  6. bencher/example/example_composable_container2.py +160 -0
  7. bencher/example/example_consts.py +39 -0
  8. bencher/example/example_custom_sweep.py +59 -0
  9. bencher/example/example_custom_sweep2.py +42 -0
  10. bencher/example/example_docs.py +34 -0
  11. bencher/example/example_filepath.py +27 -0
  12. bencher/example/example_float3D.py +101 -0
  13. bencher/example/example_float_cat.py +99 -0
  14. bencher/example/example_floats.py +89 -0
  15. bencher/example/example_floats2D.py +93 -0
  16. bencher/example/example_holosweep.py +98 -0
  17. bencher/example/example_holosweep_objects.py +111 -0
  18. bencher/example/example_holosweep_tap.py +144 -0
  19. bencher/example/example_image.py +155 -0
  20. bencher/example/example_levels.py +181 -0
  21. bencher/example/example_levels2.py +37 -0
  22. bencher/example/example_pareto.py +53 -0
  23. bencher/example/example_sample_cache.py +85 -0
  24. bencher/example/example_sample_cache_context.py +116 -0
  25. bencher/example/example_simple.py +134 -0
  26. bencher/example/example_simple_bool.py +35 -0
  27. bencher/example/example_simple_cat.py +48 -0
  28. bencher/example/example_simple_float.py +28 -0
  29. bencher/example/example_simple_float2d.py +29 -0
  30. bencher/example/example_strings.py +47 -0
  31. bencher/example/example_time_event.py +63 -0
  32. bencher/example/example_video.py +118 -0
  33. bencher/example/example_workflow.py +189 -0
  34. bencher/example/experimental/example_bokeh_plotly.py +38 -0
  35. bencher/example/experimental/example_hover_ex.py +45 -0
  36. bencher/example/experimental/example_hvplot_explorer.py +39 -0
  37. bencher/example/experimental/example_interactive.py +75 -0
  38. bencher/example/experimental/example_streamnd.py +49 -0
  39. bencher/example/experimental/example_streams.py +36 -0
  40. bencher/example/experimental/example_template.py +40 -0
  41. bencher/example/experimental/example_updates.py +84 -0
  42. bencher/example/experimental/example_vector.py +84 -0
  43. bencher/example/meta/example_meta.py +171 -0
  44. bencher/example/meta/example_meta_cat.py +25 -0
  45. bencher/example/meta/example_meta_float.py +23 -0
  46. bencher/example/meta/example_meta_levels.py +26 -0
  47. bencher/example/optuna/example_optuna.py +78 -0
  48. bencher/example/shelved/example_float2D_scatter.py +109 -0
  49. bencher/example/shelved/example_float3D_cone.py +96 -0
  50. bencher/example/shelved/example_kwargs.py +63 -0
  51. bencher/plotting/__init__.py +0 -0
  52. bencher/plotting/plot_filter.py +110 -0
  53. bencher/plotting/plt_cnt_cfg.py +75 -0
  54. bencher/results/__init__.py +0 -0
  55. bencher/results/bench_result.py +94 -0
  56. bencher/results/bench_result_base.py +476 -0
  57. bencher/results/composable_container/__init__.py +0 -0
  58. bencher/results/composable_container/composable_container_base.py +73 -0
  59. bencher/results/composable_container/composable_container_panel.py +39 -0
  60. bencher/results/composable_container/composable_container_video.py +184 -0
  61. bencher/results/float_formatter.py +44 -0
  62. bencher/results/holoview_result.py +753 -0
  63. bencher/results/optuna_result.py +354 -0
  64. bencher/results/panel_result.py +41 -0
  65. bencher/results/plotly_result.py +65 -0
  66. bencher/results/video_result.py +38 -0
  67. bencher/results/video_summary.py +222 -0
  68. bencher/variables/__init__.py +0 -0
  69. bencher/variables/inputs.py +202 -0
  70. bencher/variables/parametrised_sweep.py +208 -0
  71. bencher/variables/results.py +214 -0
  72. bencher/variables/sweep_base.py +162 -0
  73. bencher/variables/time.py +92 -0
  74. holobench-1.26.3.data/data/share/ament_index/resource_index/packages/bencher +0 -0
  75. holobench-1.26.3.data/data/share/bencher/package.xml +33 -0
  76. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/METADATA +5 -5
  77. holobench-1.26.3.dist-info/RECORD +93 -0
  78. holobench-1.25.2.dist-info/RECORD +0 -18
  79. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/LICENSE +0 -0
  80. {holobench-1.25.2.dist-info → holobench-1.26.3.dist-info}/WHEEL +0 -0
  81. {holobench-1.25.2.dist-info → holobench-1.26.3.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