evutils 0.3.10__tar.gz → 0.3.12__tar.gz
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.
- {evutils-0.3.10 → evutils-0.3.12}/.github/workflows/release.yaml +52 -3
- {evutils-0.3.10 → evutils-0.3.12}/PKG-INFO +48 -26
- {evutils-0.3.10 → evutils-0.3.12}/README.md +36 -20
- evutils-0.3.12/benchmarks/README.md +145 -0
- evutils-0.3.12/benchmarks/conftest.py +171 -0
- evutils-0.3.12/benchmarks/docker/Dockerfile.openeb +56 -0
- evutils-0.3.12/benchmarks/readers.py +144 -0
- {evutils-0.3.10 → evutils-0.3.12}/benchmarks/test_compare.py +11 -6
- evutils-0.3.12/benchmarks/test_formats.py +187 -0
- evutils-0.3.12/benchmarks/test_read.py +54 -0
- evutils-0.3.12/benchmarks/test_write.py +59 -0
- evutils-0.3.12/csrc/aer.c +70 -0
- evutils-0.3.12/csrc/dat.c +67 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/evt2.c +1 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/evt21.c +1 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/evt3.c +10 -0
- evutils-0.3.12/csrc/include/evutils/aer.h +55 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/compat.h +6 -0
- evutils-0.3.12/csrc/include/evutils/dat.h +40 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/source/conf.py +14 -5
- evutils-0.3.12/docs/source/formats.md +128 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/source/index.rst +1 -0
- {evutils-0.3.10 → evutils-0.3.12}/pyproject.toml +62 -6
- evutils-0.3.12/scripts/generate_benchmark_table.py +117 -0
- evutils-0.3.12/scripts/generate_metadata.py +102 -0
- evutils-0.3.12/src/evutils/_jit.py +34 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/augment/_drop.py +4 -3
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/chunking.py +16 -16
- evutils-0.3.12/src/evutils/io/_aedat.py +565 -0
- evutils-0.3.12/src/evutils/io/_aer.py +305 -0
- evutils-0.3.12/src/evutils/io/_bin.py +88 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/io/_csv.py +73 -21
- evutils-0.3.12/src/evutils/io/_dat.py +321 -0
- evutils-0.3.12/src/evutils/io/_event_reader.py +568 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/io/_event_writer.py +55 -32
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/io/_evt.py +224 -77
- evutils-0.3.12/src/evutils/io/_hdf5.py +364 -0
- evutils-0.3.12/src/evutils/io/_native_evt.py +1012 -0
- evutils-0.3.12/src/evutils/io/_npz.py +303 -0
- evutils-0.3.12/src/evutils/io/_prefetch.py +147 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/io/_source.py +61 -14
- evutils-0.3.12/src/evutils/io/buffer.py +203 -0
- evutils-0.3.12/src/evutils/io/common.py +321 -0
- evutils-0.3.12/src/evutils/io/decoders.py +174 -0
- evutils-0.3.12/src/evutils/io/encoders.py +85 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/processing/_masking.py +9 -2
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/processing/_utils.py +3 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/random.py +5 -8
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/_frame.py +39 -43
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/_histogram.py +28 -26
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/_timesurface.py +10 -9
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/_tore.py +16 -14
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/_voxel.py +8 -8
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/torch/__init__.py +3 -2
- evutils-0.3.12/src/evutils/types.py +299 -0
- evutils-0.3.12/src/evutils/utils/_checker.py +105 -0
- evutils-0.3.12/src/evutils/vis/__init__.py +9 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/open3d.py +13 -3
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/plot3d.py +39 -7
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/_base.py +28 -9
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/metavision.py +37 -17
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg.py +73 -21
- evutils-0.3.12/tests/aedat_synth.py +146 -0
- {evutils-0.3.10 → evutils-0.3.12}/tests/conftest.py +22 -24
- evutils-0.3.12/tests/conftest_utils.py +119 -0
- {evutils-0.3.10 → evutils-0.3.12}/tests/io/conftest.py +6 -4
- evutils-0.3.12/tests/io/test_aedat.py +145 -0
- evutils-0.3.12/tests/io/test_aer.py +98 -0
- {evutils-0.3.10 → evutils-0.3.12}/tests/io/test_csv.py +4 -3
- evutils-0.3.12/tests/io/test_dat.py +85 -0
- evutils-0.3.12/tests/io/test_evt.py +420 -0
- evutils-0.3.12/tests/io/test_hdf5.py +159 -0
- evutils-0.3.12/tests/io/test_npz.py +99 -0
- evutils-0.3.12/tests/io/test_prefetch.py +147 -0
- evutils-0.3.12/tests/io/test_roundtrip_matrix.py +162 -0
- {evutils-0.3.10 → evutils-0.3.12}/tests/test_random.py +3 -3
- {evutils-0.3.10 → evutils-0.3.12}/tests/test_types.py +2 -2
- {evutils-0.3.10 → evutils-0.3.12}/tests/test_vis.py +1 -1
- {evutils-0.3.10 → evutils-0.3.12}/uv.lock +598 -507
- evutils-0.3.10/benchmarks/README.md +0 -102
- evutils-0.3.10/benchmarks/conftest.py +0 -75
- evutils-0.3.10/benchmarks/docker/Dockerfile.openeb +0 -57
- evutils-0.3.10/benchmarks/readers.py +0 -88
- evutils-0.3.10/benchmarks/test_read.py +0 -34
- evutils-0.3.10/benchmarks/test_write.py +0 -31
- evutils-0.3.10/src/evutils/io/_aedat.py +0 -33
- evutils-0.3.10/src/evutils/io/_aer.py +0 -32
- evutils-0.3.10/src/evutils/io/_bin.py +0 -31
- evutils-0.3.10/src/evutils/io/_dat.py +0 -30
- evutils-0.3.10/src/evutils/io/_event_reader.py +0 -379
- evutils-0.3.10/src/evutils/io/_hdf5.py +0 -187
- evutils-0.3.10/src/evutils/io/_native_evt.py +0 -481
- evutils-0.3.10/src/evutils/io/_npz.py +0 -30
- evutils-0.3.10/src/evutils/io/buffer.py +0 -98
- evutils-0.3.10/src/evutils/io/common.py +0 -207
- evutils-0.3.10/src/evutils/io/decoders.py +0 -131
- evutils-0.3.10/src/evutils/io/encoders.py +0 -68
- evutils-0.3.10/src/evutils/types.py +0 -127
- evutils-0.3.10/src/evutils/utils/_checker.py +0 -32
- evutils-0.3.10/src/evutils/vis/__init__.py +0 -5
- evutils-0.3.10/tests/io/test_dat.py +0 -0
- evutils-0.3.10/tests/io/test_evt.py +0 -203
- evutils-0.3.10/tests/io/test_hdf5.py +0 -0
- evutils-0.3.10/tests/io/test_npz.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/.dockerignore +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/.github/workflows/test.yaml +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/.gitignore +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/.gitmodules +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/CMakeLists.txt +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/LICENSE +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/evutils.c +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/evt2.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/evt21.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/evt3.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/evutils.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/parser.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/csrc/include/evutils/types.h +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docker/Dockerfile +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/Makefile +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/make.bat +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/source/_static/event_hexagon_broken.webp +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/source/_templates/autoapi/python/module.rst +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/docs/source/benchmarks/README.md +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/augment/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/dataset/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/io/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/processing/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/repr/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/utils/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/.git +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/.gitignore +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/LICENSE +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/README.md +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/base/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/base/base_model.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/data/.gitignore +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/image_reconstructor.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/model/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/model/model.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/model/submodules.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/model/unet.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/options/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/options/inference_options.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/pretrained/.gitignore +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/run_reconstruction.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/scripts/embed_reconstructed_images_in_rosbag.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/scripts/extract_events_from_rosbag.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/scripts/image_folder_to_rosbag.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/scripts/resample_reconstructions.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/__init__.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/event_readers.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/inference_utils.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/loading_utils.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/path_utils.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/timers.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/src/evutils/vis/reconstructor/rpg_e2vid/utils/util.py +0 -0
- {evutils-0.3.10 → evutils-0.3.12}/tests/io/test_bin.py +0 -0
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
CIBW_ARCHS_MACOS: "x86_64 arm64"
|
|
52
52
|
|
|
53
53
|
- name: Upload wheels
|
|
54
|
-
uses: actions/upload-artifact@
|
|
54
|
+
uses: actions/upload-artifact@v7
|
|
55
55
|
with:
|
|
56
56
|
name: dist-${{ matrix.os }}
|
|
57
57
|
path: wheelhouse/*.whl
|
|
@@ -75,7 +75,7 @@ jobs:
|
|
|
75
75
|
run: pipx run build --sdist
|
|
76
76
|
|
|
77
77
|
- name: Upload sdist
|
|
78
|
-
uses: actions/upload-artifact@
|
|
78
|
+
uses: actions/upload-artifact@v7
|
|
79
79
|
with:
|
|
80
80
|
name: dist-sdist
|
|
81
81
|
path: dist/*.tar.gz
|
|
@@ -140,4 +140,53 @@ jobs:
|
|
|
140
140
|
|
|
141
141
|
- name: Deploy to GitHub Pages
|
|
142
142
|
id: deployment
|
|
143
|
-
uses: actions/deploy-pages@
|
|
143
|
+
uses: actions/deploy-pages@v5
|
|
144
|
+
|
|
145
|
+
test_pypi_wheels:
|
|
146
|
+
name: Test PyPI Wheel (${{ matrix.os }}, Py ${{ matrix.python }})
|
|
147
|
+
needs: publish_pypi
|
|
148
|
+
runs-on: ${{ matrix.os }}
|
|
149
|
+
if: github.event_name == 'release'
|
|
150
|
+
strategy:
|
|
151
|
+
fail-fast: false
|
|
152
|
+
matrix:
|
|
153
|
+
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-14]
|
|
154
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
155
|
+
steps:
|
|
156
|
+
- name: Checkout repository
|
|
157
|
+
uses: actions/checkout@v7
|
|
158
|
+
with:
|
|
159
|
+
submodules: recursive
|
|
160
|
+
|
|
161
|
+
- name: Install uv
|
|
162
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
163
|
+
with:
|
|
164
|
+
enable-cache: false
|
|
165
|
+
|
|
166
|
+
- name: Set up Python
|
|
167
|
+
uses: actions/setup-python@v6
|
|
168
|
+
with:
|
|
169
|
+
python-version: ${{ matrix.python }}
|
|
170
|
+
|
|
171
|
+
- name: Install evutils from PyPI
|
|
172
|
+
shell: bash
|
|
173
|
+
run: |
|
|
174
|
+
# Retry loop to wait for PyPI index to update with the new release
|
|
175
|
+
# GITHUB_REF_NAME contains the tag name (e.g. v1.0.0). We strip the 'v' for pip.
|
|
176
|
+
VERSION=${GITHUB_REF_NAME#v}
|
|
177
|
+
for i in {1..12}; do
|
|
178
|
+
uv pip install --system "evutils[test]==$VERSION" --no-cache && break
|
|
179
|
+
echo "Waiting for PyPI index..."
|
|
180
|
+
sleep 10
|
|
181
|
+
done
|
|
182
|
+
|
|
183
|
+
- name: Remove local source tree
|
|
184
|
+
shell: bash
|
|
185
|
+
run: |
|
|
186
|
+
# Remove local source so Python is forced to import from the installed PyPI wheel
|
|
187
|
+
rm -rf src/evutils
|
|
188
|
+
|
|
189
|
+
- name: Run tests against PyPI wheel
|
|
190
|
+
shell: bash
|
|
191
|
+
run: |
|
|
192
|
+
python -m pytest -s -v tests/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: evutils
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Summary: Utilities for event-based vision
|
|
5
5
|
Keywords: events,dvs,neuromorphic,vision,event-based
|
|
6
6
|
Author-Email: Jakub Mandula <jakub.mandula@pbl.ee.ethz.ch>
|
|
@@ -30,25 +30,31 @@ Project-URL: Homepage, https://mandulaj.github.io/evutils/
|
|
|
30
30
|
Requires-Python: >=3.10
|
|
31
31
|
Requires-Dist: numpy>=2.0.0
|
|
32
32
|
Requires-Dist: opencv-python>=4.2
|
|
33
|
+
Requires-Dist: numba>=0.57
|
|
34
|
+
Provides-Extra: aedat
|
|
35
|
+
Requires-Dist: lz4>=4.0; extra == "aedat"
|
|
36
|
+
Requires-Dist: zstandard>=0.22; python_version < "3.14" and extra == "aedat"
|
|
33
37
|
Provides-Extra: pandas
|
|
34
38
|
Requires-Dist: pandas>=2.3.3; extra == "pandas"
|
|
35
|
-
Provides-Extra: numba
|
|
36
|
-
Requires-Dist: numba>=0.57; extra == "numba"
|
|
37
39
|
Provides-Extra: torch
|
|
38
40
|
Requires-Dist: torch>=2.0; extra == "torch"
|
|
39
41
|
Provides-Extra: hdf5
|
|
40
42
|
Requires-Dist: hdf5plugin==6.0.0; extra == "hdf5"
|
|
41
|
-
Requires-Dist: h5py
|
|
43
|
+
Requires-Dist: h5py>=3.16.0; extra == "hdf5"
|
|
42
44
|
Provides-Extra: vis
|
|
43
45
|
Requires-Dist: matplotlib>=3.1; extra == "vis"
|
|
46
|
+
Requires-Dist: open3d>=0.19.0; python_version < "3.13" and extra == "vis"
|
|
44
47
|
Requires-Dist: scipy>=1.15.3; extra == "vis"
|
|
45
48
|
Requires-Dist: tqdm>=4.41; extra == "vis"
|
|
46
49
|
Provides-Extra: test
|
|
47
50
|
Requires-Dist: pytest>=9.0.2; extra == "test"
|
|
48
51
|
Requires-Dist: pytest-benchmark>=5.2.3; extra == "test"
|
|
52
|
+
Requires-Dist: zstandard>=0.22; python_version < "3.14" and extra == "test"
|
|
49
53
|
Provides-Extra: compare
|
|
50
54
|
Requires-Dist: expelliarmus>=1.1.0; extra == "compare"
|
|
51
|
-
Requires-Dist: evlib; extra == "compare"
|
|
55
|
+
Requires-Dist: evlib>=0.12.0; python_version >= "3.11" and extra == "compare"
|
|
56
|
+
Requires-Dist: evlib>=0.8.0; python_version < "3.11" and extra == "compare"
|
|
57
|
+
Requires-Dist: evt3>=0.3; extra == "compare"
|
|
52
58
|
Provides-Extra: docs
|
|
53
59
|
Requires-Dist: sphinx>=8.0.0; extra == "docs"
|
|
54
60
|
Requires-Dist: sphinx-autoapi>=3.6.1; extra == "docs"
|
|
@@ -58,7 +64,7 @@ Requires-Dist: sphinx_book_theme>=1.0.0; extra == "docs"
|
|
|
58
64
|
Requires-Dist: pydata_sphinx_theme>=0.19.0; extra == "docs"
|
|
59
65
|
Requires-Dist: myst-parser>=4.0.0; extra == "docs"
|
|
60
66
|
Provides-Extra: all
|
|
61
|
-
Requires-Dist: evutils[hdf5,
|
|
67
|
+
Requires-Dist: evutils[aedat,hdf5,pandas,torch,vis]; extra == "all"
|
|
62
68
|
Provides-Extra: dev
|
|
63
69
|
Requires-Dist: evutils[all,docs,test]; extra == "dev"
|
|
64
70
|
Description-Content-Type: text/markdown
|
|
@@ -68,7 +74,13 @@ Description-Content-Type: text/markdown
|
|
|
68
74
|
[](https://github.com/mandulaj/evutils/actions/workflows/test.yaml)
|
|
69
75
|
|
|
70
76
|
## Overview
|
|
71
|
-
EV-Utils (
|
|
77
|
+
EV-Utils (`evutils`) is a performant collection of utilities for working with event-based vision data. Built with minimal dependencies, it relies on a compiled C backend for speed while offering a clean, modular Python interface.
|
|
78
|
+
|
|
79
|
+
### Core Philosophy
|
|
80
|
+
* **Fast & Lightweight:** Highly optimized C parsers for zero-bottleneck data ingestion.
|
|
81
|
+
* **Minimal Footprint:** Core features run entirely on NumPy and Numba.
|
|
82
|
+
* **Lazy Loading:** All heavy integrations (PyTorch, Pandas, HDF5, Polars, etc.) are lazy-loaded. If you don't use them, you don't need them installed, and they won't slow down import times.
|
|
83
|
+
* **Simple & Extensible:** Clean modular APIs.
|
|
72
84
|
|
|
73
85
|
|
|
74
86
|
|
|
@@ -79,14 +91,18 @@ This project draws inspiration from several excellent libraries in the event-bas
|
|
|
79
91
|
* [event_utils](https://github.com/TimoStoff/event_utils)
|
|
80
92
|
* [evlib](https://github.com/tallamjr/evlib)
|
|
81
93
|
* [expelliarmus](https://github.com/open-neuromorphic/expelliarmus)
|
|
94
|
+
* [event-vision-library](https://github.com/shiba24/event-vision-library)
|
|
95
|
+
* [evt3](https://github.com/muthmann/evt3)
|
|
82
96
|
* [openeb](https://github.com/prophesee-ai/openeb)
|
|
83
97
|
|
|
84
98
|
|
|
85
99
|
## Installation
|
|
86
|
-
We recommend installing `evutils` using `uv`.
|
|
100
|
+
We recommend installing `evutils` using `uv`.
|
|
87
101
|
### From PyPi
|
|
88
102
|
```bash
|
|
89
|
-
uv add evutils
|
|
103
|
+
uv add evutils # Basic library
|
|
104
|
+
uv add evutils[all] # All groups (pandas, numba, torch, hdf5, etc..)
|
|
105
|
+
uv add evutils[dev] # Dev group
|
|
90
106
|
```
|
|
91
107
|
|
|
92
108
|
### From Git
|
|
@@ -131,6 +147,20 @@ Wrappers for various dataset loaders -->
|
|
|
131
147
|
|
|
132
148
|
The `io` module provides methods for reading and writing events into various event formats. It provides a simple `.read()` and `.write()` interface as well as more advanced interfaces using iterators and slicing.
|
|
133
149
|
|
|
150
|
+
Supported formats (see the [formats documentation](https://mandulaj.github.io/evutils/formats.html) for details):
|
|
151
|
+
|
|
152
|
+
| Format | Extensions | Read | Write | Notes |
|
|
153
|
+
|---|---|:---:|:---:|---|
|
|
154
|
+
| EVT3 / EVT2.1 / EVT2 (Prophesee RAW) | `.raw`, `.evt*` | ✅ | ✅ | native C decoder, external triggers |
|
|
155
|
+
| DAT (Prophesee) | `.dat` | ✅ | ✅ | native C decoder |
|
|
156
|
+
| AER (Prophesee) | `.aer` | ✅ | ✅ | timestamp generation selectable |
|
|
157
|
+
| AEDAT 1.0 / 2.0 / 3.1 / 4.0 | `.aedat`, `.aedat4` | ✅ | 🚧 | AEDAT4 compression: `evutils[aedat]` |
|
|
158
|
+
| HDF5 (DSEC/RVT layout) | `.h5`, `.hdf5` | ✅ | ✅ | `evutils[hdf5]`, ms-index random access |
|
|
159
|
+
| HDF5 (Prophesee layout) | `.h5`, `.hdf5` | ✅ | 🚧 | ECF-compressed files need the ECF plugin |
|
|
160
|
+
| NPZ | `.npz` | ✅ | ✅ | streaming, `np.load`-compatible |
|
|
161
|
+
| CSV / TXT | `.csv`, `.txt` | ✅ | ✅ | `evutils[pandas]` |
|
|
162
|
+
| BIN | `.bin` | 🚧 | 🚧 | planned |
|
|
163
|
+
|
|
134
164
|
```python
|
|
135
165
|
from evutils.io import EventReader
|
|
136
166
|
|
|
@@ -187,25 +217,17 @@ uv run pytest benchmarks/ --benchmark-group-by=param:fmt # compare libraries
|
|
|
187
217
|
|
|
188
218
|
The benchmarks download a real Prophesee recording on first use. Optional cross-library comparisons run automatically once the libraries are installed (`uv pip install -e ".[compare]"`); OpenEB/Metavision is compared via the Docker image in `benchmarks/docker/`. See [`benchmarks/README.md`](benchmarks/README.md) for details.
|
|
189
219
|
|
|
190
|
-
Decode/encode throughput on a 12th Gen Intel Core i7-1280P (millions of events per second, higher is better):
|
|
191
|
-
|
|
192
|
-
**Read** (full-file decode)
|
|
193
|
-
|
|
194
|
-
| Format | evutils | expelliarmus | evlib |
|
|
195
|
-
| ------ | ------: | -----------: | ----: |
|
|
196
|
-
| EVT2 | 91 | 105 | 5 |
|
|
197
|
-
| EVT2.1 | 72 | – | – |
|
|
198
|
-
| EVT3 | 53 | 41 | 5 |
|
|
199
|
-
|
|
200
|
-
**Write** (evutils; expelliarmus/evlib are read-only for these formats)
|
|
201
220
|
|
|
202
|
-
| Format | evutils |
|
|
203
|
-
| ------ | ------: |
|
|
204
|
-
| EVT2 | 146 |
|
|
205
|
-
| EVT2.1 | 110 |
|
|
206
|
-
| EVT3 | 74 |
|
|
207
221
|
|
|
208
|
-
|
|
222
|
+
## & Roadmap
|
|
223
|
+
We aim for universal event format support, prioritizing blazing fast read/write speeds, completeness, and extensibility.
|
|
224
|
+
- [x] Universal format support (`.raw`, `.evt2`, `.dat`, `.aedat4`, `.hdf5`, `.npz`, `.csv`, etc.)
|
|
225
|
+
- [x] Full Read/Write parity where possible
|
|
226
|
+
- [x] Chunked & Streaming access
|
|
227
|
+
- [x] External trigger data parsing
|
|
228
|
+
- [ ] **Random access / Timestamp indexing** (Big TODO for the future)
|
|
229
|
+
- [ ] **Arbitrary input sources:** memory-mapped IO, pure in-memory streams, HTTP streams
|
|
230
|
+
- [ ] **On-the-fly Compression wrappers:** passing file handles through `zstd` or `lz4` compression transparently before decoding
|
|
209
231
|
|
|
210
232
|
|
|
211
233
|
## Acknowledgements
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
[](https://github.com/mandulaj/evutils/actions/workflows/test.yaml)
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
|
-
EV-Utils (
|
|
6
|
+
EV-Utils (`evutils`) is a performant collection of utilities for working with event-based vision data. Built with minimal dependencies, it relies on a compiled C backend for speed while offering a clean, modular Python interface.
|
|
7
|
+
|
|
8
|
+
### Core Philosophy
|
|
9
|
+
* **Fast & Lightweight:** Highly optimized C parsers for zero-bottleneck data ingestion.
|
|
10
|
+
* **Minimal Footprint:** Core features run entirely on NumPy and Numba.
|
|
11
|
+
* **Lazy Loading:** All heavy integrations (PyTorch, Pandas, HDF5, Polars, etc.) are lazy-loaded. If you don't use them, you don't need them installed, and they won't slow down import times.
|
|
12
|
+
* **Simple & Extensible:** Clean modular APIs.
|
|
7
13
|
|
|
8
14
|
|
|
9
15
|
|
|
@@ -14,14 +20,18 @@ This project draws inspiration from several excellent libraries in the event-bas
|
|
|
14
20
|
* [event_utils](https://github.com/TimoStoff/event_utils)
|
|
15
21
|
* [evlib](https://github.com/tallamjr/evlib)
|
|
16
22
|
* [expelliarmus](https://github.com/open-neuromorphic/expelliarmus)
|
|
23
|
+
* [event-vision-library](https://github.com/shiba24/event-vision-library)
|
|
24
|
+
* [evt3](https://github.com/muthmann/evt3)
|
|
17
25
|
* [openeb](https://github.com/prophesee-ai/openeb)
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
## Installation
|
|
21
|
-
We recommend installing `evutils` using `uv`.
|
|
29
|
+
We recommend installing `evutils` using `uv`.
|
|
22
30
|
### From PyPi
|
|
23
31
|
```bash
|
|
24
|
-
uv add evutils
|
|
32
|
+
uv add evutils # Basic library
|
|
33
|
+
uv add evutils[all] # All groups (pandas, numba, torch, hdf5, etc..)
|
|
34
|
+
uv add evutils[dev] # Dev group
|
|
25
35
|
```
|
|
26
36
|
|
|
27
37
|
### From Git
|
|
@@ -66,6 +76,20 @@ Wrappers for various dataset loaders -->
|
|
|
66
76
|
|
|
67
77
|
The `io` module provides methods for reading and writing events into various event formats. It provides a simple `.read()` and `.write()` interface as well as more advanced interfaces using iterators and slicing.
|
|
68
78
|
|
|
79
|
+
Supported formats (see the [formats documentation](https://mandulaj.github.io/evutils/formats.html) for details):
|
|
80
|
+
|
|
81
|
+
| Format | Extensions | Read | Write | Notes |
|
|
82
|
+
|---|---|:---:|:---:|---|
|
|
83
|
+
| EVT3 / EVT2.1 / EVT2 (Prophesee RAW) | `.raw`, `.evt*` | ✅ | ✅ | native C decoder, external triggers |
|
|
84
|
+
| DAT (Prophesee) | `.dat` | ✅ | ✅ | native C decoder |
|
|
85
|
+
| AER (Prophesee) | `.aer` | ✅ | ✅ | timestamp generation selectable |
|
|
86
|
+
| AEDAT 1.0 / 2.0 / 3.1 / 4.0 | `.aedat`, `.aedat4` | ✅ | 🚧 | AEDAT4 compression: `evutils[aedat]` |
|
|
87
|
+
| HDF5 (DSEC/RVT layout) | `.h5`, `.hdf5` | ✅ | ✅ | `evutils[hdf5]`, ms-index random access |
|
|
88
|
+
| HDF5 (Prophesee layout) | `.h5`, `.hdf5` | ✅ | 🚧 | ECF-compressed files need the ECF plugin |
|
|
89
|
+
| NPZ | `.npz` | ✅ | ✅ | streaming, `np.load`-compatible |
|
|
90
|
+
| CSV / TXT | `.csv`, `.txt` | ✅ | ✅ | `evutils[pandas]` |
|
|
91
|
+
| BIN | `.bin` | 🚧 | 🚧 | planned |
|
|
92
|
+
|
|
69
93
|
```python
|
|
70
94
|
from evutils.io import EventReader
|
|
71
95
|
|
|
@@ -122,25 +146,17 @@ uv run pytest benchmarks/ --benchmark-group-by=param:fmt # compare libraries
|
|
|
122
146
|
|
|
123
147
|
The benchmarks download a real Prophesee recording on first use. Optional cross-library comparisons run automatically once the libraries are installed (`uv pip install -e ".[compare]"`); OpenEB/Metavision is compared via the Docker image in `benchmarks/docker/`. See [`benchmarks/README.md`](benchmarks/README.md) for details.
|
|
124
148
|
|
|
125
|
-
Decode/encode throughput on a 12th Gen Intel Core i7-1280P (millions of events per second, higher is better):
|
|
126
|
-
|
|
127
|
-
**Read** (full-file decode)
|
|
128
|
-
|
|
129
|
-
| Format | evutils | expelliarmus | evlib |
|
|
130
|
-
| ------ | ------: | -----------: | ----: |
|
|
131
|
-
| EVT2 | 91 | 105 | 5 |
|
|
132
|
-
| EVT2.1 | 72 | – | – |
|
|
133
|
-
| EVT3 | 53 | 41 | 5 |
|
|
134
|
-
|
|
135
|
-
**Write** (evutils; expelliarmus/evlib are read-only for these formats)
|
|
136
149
|
|
|
137
|
-
| Format | evutils |
|
|
138
|
-
| ------ | ------: |
|
|
139
|
-
| EVT2 | 146 |
|
|
140
|
-
| EVT2.1 | 110 |
|
|
141
|
-
| EVT3 | 74 |
|
|
142
150
|
|
|
143
|
-
|
|
151
|
+
## & Roadmap
|
|
152
|
+
We aim for universal event format support, prioritizing blazing fast read/write speeds, completeness, and extensibility.
|
|
153
|
+
- [x] Universal format support (`.raw`, `.evt2`, `.dat`, `.aedat4`, `.hdf5`, `.npz`, `.csv`, etc.)
|
|
154
|
+
- [x] Full Read/Write parity where possible
|
|
155
|
+
- [x] Chunked & Streaming access
|
|
156
|
+
- [x] External trigger data parsing
|
|
157
|
+
- [ ] **Random access / Timestamp indexing** (Big TODO for the future)
|
|
158
|
+
- [ ] **Arbitrary input sources:** memory-mapped IO, pure in-memory streams, HTTP streams
|
|
159
|
+
- [ ] **On-the-fly Compression wrappers:** passing file handles through `zstd` or `lz4` compression transparently before decoding
|
|
144
160
|
|
|
145
161
|
|
|
146
162
|
## Acknowledgements
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
Read/write throughput benchmarks for the native EVT2 / EVT2.1 / EVT3 codecs, plus optional comparisons against other event libraries.
|
|
4
|
+
|
|
5
|
+
These benchmarks are **not** part of the normal test run (which uses `testpaths = ["tests"]`). They must be executed explicitly from the repository root.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
Run the default suite (evutils only, plus any installed comparison libraries):
|
|
10
|
+
```bash
|
|
11
|
+
pytest benchmarks/
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run specific benchmarks:
|
|
15
|
+
```bash
|
|
16
|
+
pytest benchmarks/test_read.py # just reads
|
|
17
|
+
pytest benchmarks/test_write.py # just writes
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Benchmark Options
|
|
21
|
+
|
|
22
|
+
### Datasets
|
|
23
|
+
You can benchmark against two datasets using the `--dataset` flag. The necessary files are automatically downloaded and cached on first use.
|
|
24
|
+
- `--dataset small` (default): A ~1GB memory footprint dataset (hand recordings).
|
|
25
|
+
- `--dataset large`: A massive multi-GB dataset designed to test the streaming and chunking capabilities of the decoders.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pytest benchmarks/ --dataset large
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Filtering Readers
|
|
32
|
+
The benchmark tests are fully parametrized by the reader name. You can effortlessly exclude specific third-party libraries using pytest's standard `-k` flag (for example, if they are slow or misbehaving on large datasets):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pytest benchmarks/ -k "not evlib" --dataset large
|
|
36
|
+
pytest benchmarks/ -k "not evlib and not openeb"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## File Structure
|
|
40
|
+
|
|
41
|
+
| File | What it benchmarks |
|
|
42
|
+
|------|--------------------|
|
|
43
|
+
| `test_read.py` | `evutils` decode throughput on the full real recordings (evt2/evt21/evt3), asserts count vs reference |
|
|
44
|
+
| `test_write.py` | `evutils` encode throughput (payload = first 5M events of the real evt3 file) |
|
|
45
|
+
| `test_formats.py` | **uniform per-format read/write**: the same 5M real events transcoded into every format (EVT3/EVT2.1/EVT2, DAT, AER, NPZ, HDF5, CSV, AEDAT4), so numbers are comparable across formats; expelliarmus (DAT) and evlib (HDF5, AEDAT4) compared on the identical files |
|
|
46
|
+
| `test_compare.py` | third-party readers from `readers.py` on the full real recordings (auto-skip if not installed) |
|
|
47
|
+
| `readers.py` | adapter registry — one entry per external library |
|
|
48
|
+
|
|
49
|
+
*Note: in `test_formats.py` AER is the one lossy transcode — the format has no timestamps and 9-bit coordinates, so the same events are written with coordinates masked to 0–511 (identical event count).*
|
|
50
|
+
|
|
51
|
+
## Comparing Against Other Libraries
|
|
52
|
+
|
|
53
|
+
Install the optional readers and run with grouping so every library lines up per format:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install evutils[compare] # expelliarmus, evlib
|
|
57
|
+
pytest benchmarks/ --benchmark-group-by=param:fmt --benchmark-columns=mean,ops
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Each library reads inside a lazy import. If a library is uninstalled or broken, its benchmarks simply **skip**. To add another library, append a `Reader(...)` entry to `readers.py`.
|
|
61
|
+
|
|
62
|
+
## Benchmark Comparison (Mean Time in Seconds)
|
|
63
|
+
|
|
64
|
+
### Reading
|
|
65
|
+
|
|
66
|
+
| Library | EVT2 | EVT21 | EVT3 |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| **evutils** | 0.138 s | 0.070 s | 0.292 s |
|
|
69
|
+
| **evlib** | 4.410 s | N/A | 4.327 s |
|
|
70
|
+
| **expelliarmus** | 0.128 s | N/A | 0.341 s |
|
|
71
|
+
|
|
72
|
+
### Writing
|
|
73
|
+
|
|
74
|
+
| Library | EVT2 | EVT21 | EVT3 |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| **evutils** | 0.013 s | 0.028 s | 0.041 s |
|
|
77
|
+
| **expelliarmus** | 0.077 s | N/A | 0.097 s |
|
|
78
|
+
|
|
79
|
+
**Hardware:** 12th Gen Intel(R) Core(TM) i7-1280P | **OS:** Linux 7.1.1-3-MANJARO | **Python:** 3.12.13
|
|
80
|
+
|
|
81
|
+
*Lower is better. Generated dynamically by `scripts/generate_benchmark_table.py`.*
|
|
82
|
+
|
|
83
|
+
> **Note**: `tonic` is intentionally not included. It has no standalone EVT reader and reads Prophesee data through `expelliarmus` internally, so benchmarking it would just re-measure `expelliarmus`.
|
|
84
|
+
|
|
85
|
+
## OpenEB / Metavision (via Docker)
|
|
86
|
+
|
|
87
|
+
OpenEB isn't on PyPI and is painful to build locally, so there's an image that builds it once. Both commands must be run **from the repo root** (the build context must be the whole project so `evutils` is copied in):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Build the image (compiles OpenEB from source + installs evutils; slow, one-time)
|
|
91
|
+
docker build -t evutils-openeb -f benchmarks/docker/Dockerfile.openeb .
|
|
92
|
+
|
|
93
|
+
# Run the full suite (evutils + OpenEB), grouped per format
|
|
94
|
+
docker run --rm evutils-openeb
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The container's default command is `pytest benchmarks/ --benchmark-group-by=param:fmt`, so you get `evutils` and OpenEB side by side per format.
|
|
98
|
+
|
|
99
|
+
### Useful Variations
|
|
100
|
+
|
|
101
|
+
The recordings are **not** baked into the image (`.dockerignore` excludes the
|
|
102
|
+
data and cache directories), so the fixture tries to download them on first
|
|
103
|
+
use. If the container has no network access, mount the host's already-cached
|
|
104
|
+
recordings and point `EVUTILS_BENCH_DATA` at them:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# after benchmarks have run at least once on the host:
|
|
108
|
+
docker run --rm \
|
|
109
|
+
-v "$(pwd)/.pytest_cache/d/event_files:/data:ro" \
|
|
110
|
+
-e EVUTILS_BENCH_DATA=/data \
|
|
111
|
+
evutils-openeb
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Alternatively, persist the container's own download across runs with a named
|
|
115
|
+
volume (note the workdir is `/home/user/work`):
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
docker run --rm -v evutils-cache:/home/user/work/.pytest_cache evutils-openeb
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run only the OpenEB comparison (skip the `evutils` rows):
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
docker run --rm evutils-openeb \
|
|
125
|
+
pytest benchmarks/test_compare.py --benchmark-group-by=param:fmt -q
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Drop into a shell to debug the build/run:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
docker run --rm -it evutils-openeb bash
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Build a different OpenEB release if the default fails to build:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
docker build -t evutils-openeb --build-arg OPENEB_VERSION=5.0.0 \
|
|
138
|
+
-f benchmarks/docker/Dockerfile.openeb .
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Caveats
|
|
142
|
+
|
|
143
|
+
- The image targets **OpenEB 5.x on Ubuntu 22.04**; OpenEB's apt dependencies and install layout drift between releases, so the `apt-get`/`PYTHONPATH` lines may need tweaking. The Dockerfile imports `metavision_core` at build time, so a broken OpenEB install fails during `docker build` rather than silently skipping at run time.
|
|
144
|
+
- The first pass is slow: it compiles OpenEB (`-j$(nproc)`) and downloads the reference recording on first run.
|
|
145
|
+
- A repo-root `.dockerignore` keeps the build context small (excludes `.venv`, `build/`, `.git`, `data/`, `*.raw`, etc.).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Benchmark fixtures.
|
|
2
|
+
|
|
3
|
+
``EventFile`` and ``real_event_files`` are duplicated from ``tests/conftest.py``
|
|
4
|
+
on purpose: pytest only shares conftest fixtures downward, and ``benchmarks/``
|
|
5
|
+
is a sibling of ``tests/``, so there is no clean way to reuse them without a
|
|
6
|
+
repo-root conftest. Keep the two copies in sync.
|
|
7
|
+
"""
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
from typing import Any, cast
|
|
14
|
+
|
|
15
|
+
from evutils.io import EventReader
|
|
16
|
+
|
|
17
|
+
sys.path.append(str(Path(__file__).parent.parent / "tests"))
|
|
18
|
+
from conftest_utils import EventFile, download_and_extract_gdrive, load_event_files # type: ignore
|
|
19
|
+
|
|
20
|
+
#: Cap on how many events are held in memory for the write benchmarks. Large
|
|
21
|
+
#: enough for a stable throughput measurement, small enough to stay light.
|
|
22
|
+
N_REFERENCE = 5_000_000
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture(scope='session')
|
|
26
|
+
def real_event_files(request: Any, benchmark_dataset: str) -> dict[str, list[Any]]:
|
|
27
|
+
"""Real Prophesee recordings (downloaded + cached on first use).
|
|
28
|
+
|
|
29
|
+
Returns ``{format: [EventFile(path, count)]}``.
|
|
30
|
+
|
|
31
|
+
Set ``EVUTILS_BENCH_DATA`` to a directory that already contains the
|
|
32
|
+
extracted recordings (+ JSON metadata) to skip the download entirely --
|
|
33
|
+
useful in offline environments such as the OpenEB benchmark container
|
|
34
|
+
(mount the host cache there).
|
|
35
|
+
"""
|
|
36
|
+
import os
|
|
37
|
+
override = os.environ.get("EVUTILS_BENCH_DATA")
|
|
38
|
+
if override:
|
|
39
|
+
data_dir = Path(override)
|
|
40
|
+
if not data_dir.is_dir():
|
|
41
|
+
pytest.skip(f"EVUTILS_BENCH_DATA={override} is not a directory")
|
|
42
|
+
return cast(dict[str, list[Any]], load_event_files(data_dir))
|
|
43
|
+
|
|
44
|
+
if benchmark_dataset == "large":
|
|
45
|
+
temp_dir = request.config.cache.mkdir("event_files_huge")
|
|
46
|
+
file_id = "1QPuilR1VD0rKyhVOlu1Y-HtkO4ZBc2Cz"
|
|
47
|
+
|
|
48
|
+
json_files = list(temp_dir.glob("*.json"))
|
|
49
|
+
if not json_files:
|
|
50
|
+
download_and_extract_gdrive(file_id, temp_dir, "huge.tar.zst")
|
|
51
|
+
|
|
52
|
+
return cast(dict[str, list[Any]], load_event_files(temp_dir))
|
|
53
|
+
else:
|
|
54
|
+
temp_dir = request.config.cache.mkdir("event_files")
|
|
55
|
+
file_id = "1uhOsWbp2o3CktsHrFkzGCNFbx0bQLsct"
|
|
56
|
+
filenames = {
|
|
57
|
+
'evt3': "hand_evt3.raw",
|
|
58
|
+
'evt21': "hand_evt21.raw",
|
|
59
|
+
'evt2': "hand_evt2.raw",
|
|
60
|
+
}
|
|
61
|
+
paths = {fmt: temp_dir / name for fmt, name in filenames.items()}
|
|
62
|
+
|
|
63
|
+
for key, path in paths.items():
|
|
64
|
+
if not path.exists():
|
|
65
|
+
download_and_extract_gdrive(file_id, temp_dir, "hand.tar.zst")
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
return cast(dict[str, list[Any]], load_event_files(temp_dir))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.fixture(scope='session')
|
|
72
|
+
def reference_events(real_event_files: dict[str, list[Any]]) -> Any:
|
|
73
|
+
"""First ``N_REFERENCE`` events of the real EVT3 recording as an AoS array.
|
|
74
|
+
|
|
75
|
+
Decoded once and reused as the payload for every write benchmark, so the
|
|
76
|
+
write benchmarks measure encoding cost only (not decoding).
|
|
77
|
+
"""
|
|
78
|
+
parts = []
|
|
79
|
+
total = 0
|
|
80
|
+
if 'evt3' not in real_event_files or not real_event_files['evt3']:
|
|
81
|
+
pytest.skip("No evt3 files available for reference events")
|
|
82
|
+
# Use the hand_evt3.raw file for reference events
|
|
83
|
+
ef = next((f for f in real_event_files['evt3'] if 'hand' in f.path.name), real_event_files['evt3'][0])
|
|
84
|
+
with EventReader(ef.path, n_events=1_000_000) as reader:
|
|
85
|
+
for chunk in reader:
|
|
86
|
+
parts.append(chunk.to_aos())
|
|
87
|
+
total += len(chunk)
|
|
88
|
+
if total >= N_REFERENCE:
|
|
89
|
+
break
|
|
90
|
+
return np.concatenate(parts)[:N_REFERENCE]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def pytest_addoption(parser: Any) -> None:
|
|
94
|
+
parser.addoption(
|
|
95
|
+
"--rounds", action="store", default=4, type=int, help="Number of benchmark rounds"
|
|
96
|
+
)
|
|
97
|
+
parser.addoption(
|
|
98
|
+
"--dataset", action="store", default="small", choices=["small", "large"],
|
|
99
|
+
help="Dataset size to use for benchmarks ('small' or 'large')"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@pytest.fixture(scope="session")
|
|
103
|
+
def benchmark_rounds(request: Any) -> int:
|
|
104
|
+
return int(request.config.getoption("--rounds"))
|
|
105
|
+
|
|
106
|
+
@pytest.fixture(scope="session")
|
|
107
|
+
def benchmark_dataset(request: Any) -> str:
|
|
108
|
+
return str(request.config.getoption("--dataset"))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.hookimpl(hookwrapper=True)
|
|
112
|
+
def pytest_benchmark_group_stats(config: Any, benchmarks: list[dict[str, Any]], group_by: str) -> Any:
|
|
113
|
+
"""Guard against a pytest-benchmark crash with ``--benchmark-group-by=param:<name>``.
|
|
114
|
+
|
|
115
|
+
Benchmarks collected from non-parametrized tests have ``params = None``,
|
|
116
|
+
which the plugin's param-grouping code cannot handle (TypeError) -- and the
|
|
117
|
+
crash also swallows the whole error/summary report. Give such benchmarks an
|
|
118
|
+
explicit null param instead, so they group under ``<name>=None``.
|
|
119
|
+
"""
|
|
120
|
+
if isinstance(group_by, str) and group_by.startswith("param:"):
|
|
121
|
+
names = group_by.split(":", 1)[1].split(",")
|
|
122
|
+
for bench in benchmarks:
|
|
123
|
+
if bench.get("params") is None:
|
|
124
|
+
bench["params"] = {}
|
|
125
|
+
for name in names:
|
|
126
|
+
bench["params"].setdefault(name, None)
|
|
127
|
+
yield
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@pytest.fixture(scope="session")
|
|
131
|
+
def uniform_files(reference_events: Any, tmp_path_factory: Any) -> dict[str, Any]:
|
|
132
|
+
"""The same ``N_REFERENCE`` real events transcoded into every format.
|
|
133
|
+
|
|
134
|
+
All per-format read/write benchmarks (benchmarks/test_formats.py) operate
|
|
135
|
+
on these files, so throughput numbers are comparable across formats: same
|
|
136
|
+
events, same count. AER is the one lossy exception -- it has no timestamps
|
|
137
|
+
and 9-bit coordinates, so its file carries the same events with coordinates
|
|
138
|
+
masked to 0..511 (the event count is identical).
|
|
139
|
+
"""
|
|
140
|
+
from evutils.io import EventWriter
|
|
141
|
+
|
|
142
|
+
sys.path.append(str(Path(__file__).parent.parent / "tests"))
|
|
143
|
+
from aedat_synth import make_aedat4 # type: ignore
|
|
144
|
+
|
|
145
|
+
d = tmp_path_factory.mktemp("uniform")
|
|
146
|
+
ev = reference_events
|
|
147
|
+
files = {}
|
|
148
|
+
|
|
149
|
+
for fmt in ("evt3", "evt21", "evt2"):
|
|
150
|
+
files[fmt] = d / f"ref_{fmt}.raw"
|
|
151
|
+
with EventWriter(files[fmt], width=1280, height=720, format=fmt) as w:
|
|
152
|
+
w.write(ev)
|
|
153
|
+
|
|
154
|
+
for fmt, name in (("dat", "ref.dat"), ("npz", "ref.npz"),
|
|
155
|
+
("hdf5", "ref.h5"), ("csv", "ref.csv")):
|
|
156
|
+
files[fmt] = d / name
|
|
157
|
+
with EventWriter(files[fmt], width=1280, height=720) as w:
|
|
158
|
+
w.write(ev)
|
|
159
|
+
|
|
160
|
+
aer = ev.copy()
|
|
161
|
+
aer["x"] &= 0x1FF
|
|
162
|
+
aer["y"] &= 0x1FF
|
|
163
|
+
files["aer"] = d / "ref.aer"
|
|
164
|
+
with EventWriter(files["aer"]) as w:
|
|
165
|
+
w.write(aer)
|
|
166
|
+
|
|
167
|
+
files["aedat4"] = d / "ref.aedat4"
|
|
168
|
+
files["aedat4"].write_bytes(
|
|
169
|
+
make_aedat4(ev["t"], ev["x"], ev["y"], ev["p"], events_per_packet=65536)
|
|
170
|
+
)
|
|
171
|
+
return files
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# OpenEB (Metavision SDK, open-source edition) + evutils, for running the
|
|
2
|
+
# comparison benchmarks against OpenEB's RawReader.
|
|
3
|
+
#
|
|
4
|
+
# OpenEB is not on PyPI and is fiddly to build; this image does it once.
|
|
5
|
+
#
|
|
6
|
+
# Build (from the repo root, so the whole project is the build context):
|
|
7
|
+
# docker build -t evutils-openeb -f benchmarks/docker/Dockerfile.openeb .
|
|
8
|
+
#
|
|
9
|
+
# Run the benchmarks (downloads the reference recordings on first run):
|
|
10
|
+
# docker run --rm evutils-openeb
|
|
11
|
+
#
|
|
12
|
+
# Tip: add a repo-root .dockerignore (.venv, build/, .git, *.raw, data/) to keep
|
|
13
|
+
# the build context small.
|
|
14
|
+
#
|
|
15
|
+
# NOTE: OpenEB's exact apt dependencies and layout shift between releases. This
|
|
16
|
+
# targets OpenEB 5.x on Ubuntu 22.04 -- bump OPENEB_VERSION / tweak deps if the
|
|
17
|
+
# build fails.
|
|
18
|
+
FROM registry.git.ee.ethz.ch/pbl/research/event-camera/docker/open-eb-docker/openeb_base:latest
|
|
19
|
+
|
|
20
|
+
ARG USERNAME=user
|
|
21
|
+
ARG PASSWORD=password
|
|
22
|
+
ARG USER_ID=1000
|
|
23
|
+
ARG GROUP_ID=1000
|
|
24
|
+
|
|
25
|
+
# Validate that all required arguments are set
|
|
26
|
+
RUN : \
|
|
27
|
+
&& if [ -z "$USERNAME" ]; then echo "ERROR: USERNAME is not set."; exit 1; fi \
|
|
28
|
+
&& if [ -z "$PASSWORD" ]; then echo "ERROR: PASSWORD is not set."; exit 1; fi \
|
|
29
|
+
&& if [ -z "$USER_ID" ]; then echo "ERROR: USER_ID is not set."; exit 1; fi \
|
|
30
|
+
&& if [ -z "$GROUP_ID" ]; then echo "ERROR: GROUP_ID is not set."; exit 1; fi
|
|
31
|
+
|
|
32
|
+
RUN groupadd -g ${GROUP_ID} ${USERNAME} && useradd -u ${USER_ID} -g ${GROUP_ID} -ms /bin/bash ${USERNAME} && usermod -aG sudo ${USERNAME} && echo "${USERNAME}:${PASSWORD}" | chpasswd
|
|
33
|
+
|
|
34
|
+
USER ${USERNAME}
|
|
35
|
+
RUN echo "source /openeb/build/utils/scripts/setup_env.sh" >> /home/${USERNAME}/.bashrc
|
|
36
|
+
|
|
37
|
+
WORKDIR /home/${USERNAME}/work
|
|
38
|
+
COPY . /home/${USERNAME}/work
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# 2. Omit --python 3.12 so it defaults to the system Python (3.10) to match OpenEB
|
|
43
|
+
RUN uv venv --system-site-packages
|
|
44
|
+
RUN uv pip install --no-cache-dir -e ".[test,compare]"
|
|
45
|
+
|
|
46
|
+
ENV PATH="/home/${USERNAME}/work/.venv/bin:$PATH"
|
|
47
|
+
|
|
48
|
+
# 3. Source the OpenEB setup script directly during the RUN step
|
|
49
|
+
RUN . /openeb/build/utils/scripts/setup_env.sh && \
|
|
50
|
+
python3 -c "from metavision_core.event_io import RawReader; print('OpenEB OK')"
|
|
51
|
+
|
|
52
|
+
# 4. Use an ENTRYPOINT script to ensure OpenEB vars are loaded for ANY command you run
|
|
53
|
+
ENTRYPOINT ["/bin/bash", "-c", "source /openeb/build/utils/scripts/setup_env.sh && exec \"$@\"", "--"]
|
|
54
|
+
|
|
55
|
+
CMD ["pytest", "benchmarks/", "--benchmark-group-by=param:fmt", \
|
|
56
|
+
"--benchmark-columns=mean,ops", "-q"]
|