evutils 0.3.7__tar.gz → 0.3.11__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.
Files changed (148) hide show
  1. evutils-0.3.11/.github/workflows/release.yaml +143 -0
  2. {evutils-0.3.7 → evutils-0.3.11}/.github/workflows/test.yaml +1 -0
  3. {evutils-0.3.7 → evutils-0.3.11}/PKG-INFO +11 -28
  4. {evutils-0.3.7 → evutils-0.3.11}/README.md +4 -22
  5. evutils-0.3.11/benchmarks/README.md +131 -0
  6. evutils-0.3.11/benchmarks/conftest.py +94 -0
  7. evutils-0.3.11/benchmarks/docker/Dockerfile.openeb +56 -0
  8. {evutils-0.3.7 → evutils-0.3.11}/benchmarks/readers.py +14 -3
  9. {evutils-0.3.7 → evutils-0.3.11}/benchmarks/test_compare.py +6 -3
  10. evutils-0.3.11/benchmarks/test_fixed_formats.py +119 -0
  11. {evutils-0.3.7 → evutils-0.3.11}/benchmarks/test_read.py +14 -10
  12. evutils-0.3.11/benchmarks/test_write.py +57 -0
  13. evutils-0.3.11/csrc/aer.c +36 -0
  14. evutils-0.3.11/csrc/dat.c +66 -0
  15. {evutils-0.3.7 → evutils-0.3.11}/csrc/evt3.c +4 -0
  16. evutils-0.3.11/csrc/include/evutils/aer.h +35 -0
  17. evutils-0.3.11/csrc/include/evutils/compat.h +55 -0
  18. evutils-0.3.11/csrc/include/evutils/dat.h +40 -0
  19. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/parser.h +3 -2
  20. {evutils-0.3.7 → evutils-0.3.11}/docs/source/conf.py +14 -5
  21. {evutils-0.3.7 → evutils-0.3.11}/pyproject.toml +37 -6
  22. evutils-0.3.11/scripts/generate_benchmark_table.py +117 -0
  23. evutils-0.3.11/scripts/generate_metadata.py +102 -0
  24. evutils-0.3.11/src/evutils/_jit.py +34 -0
  25. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/augment/_drop.py +3 -2
  26. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/chunking.py +11 -12
  27. evutils-0.3.11/src/evutils/io/_aedat.py +86 -0
  28. evutils-0.3.11/src/evutils/io/_aer.py +245 -0
  29. evutils-0.3.11/src/evutils/io/_bin.py +86 -0
  30. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/_csv.py +66 -14
  31. evutils-0.3.11/src/evutils/io/_dat.py +320 -0
  32. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/_event_reader.py +203 -72
  33. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/_event_writer.py +49 -26
  34. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/_evt.py +217 -70
  35. evutils-0.3.11/src/evutils/io/_hdf5.py +295 -0
  36. evutils-0.3.11/src/evutils/io/_native_evt.py +981 -0
  37. evutils-0.3.11/src/evutils/io/_npz.py +192 -0
  38. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/_source.py +48 -2
  39. evutils-0.3.11/src/evutils/io/buffer.py +203 -0
  40. evutils-0.3.11/src/evutils/io/common.py +321 -0
  41. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/decoders.py +61 -37
  42. evutils-0.3.11/src/evutils/io/encoders.py +85 -0
  43. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/processing/_masking.py +8 -1
  44. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/processing/_utils.py +3 -0
  45. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/random.py +4 -7
  46. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/_frame.py +32 -37
  47. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/_histogram.py +23 -22
  48. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/_timesurface.py +6 -6
  49. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/_tore.py +12 -11
  50. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/_voxel.py +6 -7
  51. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/torch/__init__.py +1 -1
  52. evutils-0.3.11/src/evutils/types.py +298 -0
  53. evutils-0.3.11/src/evutils/utils/_checker.py +105 -0
  54. evutils-0.3.11/src/evutils/vis/__init__.py +9 -0
  55. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/open3d.py +12 -2
  56. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/plot3d.py +32 -1
  57. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/reconstructor/_base.py +23 -6
  58. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/reconstructor/metavision.py +34 -15
  59. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/reconstructor/rpg.py +51 -5
  60. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/.git +1 -0
  61. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/.gitignore +1 -0
  62. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/LICENSE +674 -0
  63. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/README.md +179 -0
  64. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/base/__init__.py +1 -0
  65. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/base/base_model.py +30 -0
  66. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/image_reconstructor.py +112 -0
  67. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/model/model.py +100 -0
  68. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/model/submodules.py +275 -0
  69. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/model/unet.py +178 -0
  70. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/options/inference_options.py +66 -0
  71. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/pretrained/.gitignore +0 -0
  72. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/run_reconstruction.py +112 -0
  73. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/scripts/embed_reconstructed_images_in_rosbag.py +90 -0
  74. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/scripts/extract_events_from_rosbag.py +126 -0
  75. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/scripts/image_folder_to_rosbag.py +73 -0
  76. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/scripts/resample_reconstructions.py +56 -0
  77. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/__init__.py +0 -0
  78. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/event_readers.py +88 -0
  79. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/inference_utils.py +545 -0
  80. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/loading_utils.py +31 -0
  81. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/path_utils.py +6 -0
  82. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/timers.py +57 -0
  83. evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/utils/util.py +50 -0
  84. {evutils-0.3.7 → evutils-0.3.11}/tests/conftest.py +7 -22
  85. evutils-0.3.11/tests/conftest_utils.py +69 -0
  86. evutils-0.3.11/tests/io/test_aer.py +32 -0
  87. evutils-0.3.11/tests/io/test_bin.py +0 -0
  88. evutils-0.3.11/tests/io/test_dat.py +44 -0
  89. evutils-0.3.11/tests/io/test_evt.py +303 -0
  90. evutils-0.3.11/tests/io/test_hdf5.py +0 -0
  91. evutils-0.3.11/tests/io/test_npz.py +0 -0
  92. {evutils-0.3.7 → evutils-0.3.11}/uv.lock +598 -507
  93. evutils-0.3.7/.github/workflows/release.yaml +0 -81
  94. evutils-0.3.7/benchmarks/README.md +0 -102
  95. evutils-0.3.7/benchmarks/conftest.py +0 -75
  96. evutils-0.3.7/benchmarks/docker/Dockerfile.openeb +0 -57
  97. evutils-0.3.7/benchmarks/test_write.py +0 -31
  98. evutils-0.3.7/src/evutils/io/_aedat.py +0 -33
  99. evutils-0.3.7/src/evutils/io/_aer.py +0 -32
  100. evutils-0.3.7/src/evutils/io/_bin.py +0 -31
  101. evutils-0.3.7/src/evutils/io/_dat.py +0 -30
  102. evutils-0.3.7/src/evutils/io/_hdf5.py +0 -187
  103. evutils-0.3.7/src/evutils/io/_native_evt.py +0 -481
  104. evutils-0.3.7/src/evutils/io/_npz.py +0 -30
  105. evutils-0.3.7/src/evutils/io/buffer.py +0 -98
  106. evutils-0.3.7/src/evutils/io/common.py +0 -207
  107. evutils-0.3.7/src/evutils/io/encoders.py +0 -68
  108. evutils-0.3.7/src/evutils/types.py +0 -127
  109. evutils-0.3.7/src/evutils/utils/_checker.py +0 -32
  110. evutils-0.3.7/src/evutils/vis/__init__.py +0 -5
  111. evutils-0.3.7/tests/io/test_evt.py +0 -203
  112. {evutils-0.3.7 → evutils-0.3.11}/.dockerignore +0 -0
  113. {evutils-0.3.7 → evutils-0.3.11}/.gitignore +0 -0
  114. {evutils-0.3.7 → evutils-0.3.11}/.gitmodules +0 -0
  115. {evutils-0.3.7 → evutils-0.3.11}/CMakeLists.txt +0 -0
  116. {evutils-0.3.7 → evutils-0.3.11}/LICENSE +0 -0
  117. {evutils-0.3.7 → evutils-0.3.11}/csrc/evt2.c +0 -0
  118. {evutils-0.3.7 → evutils-0.3.11}/csrc/evt21.c +0 -0
  119. {evutils-0.3.7 → evutils-0.3.11}/csrc/evutils.c +0 -0
  120. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/evt2.h +0 -0
  121. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/evt21.h +0 -0
  122. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/evt3.h +0 -0
  123. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/evutils.h +0 -0
  124. {evutils-0.3.7 → evutils-0.3.11}/csrc/include/evutils/types.h +0 -0
  125. {evutils-0.3.7 → evutils-0.3.11}/docker/Dockerfile +0 -0
  126. {evutils-0.3.7 → evutils-0.3.11}/docs/Makefile +0 -0
  127. {evutils-0.3.7 → evutils-0.3.11}/docs/make.bat +0 -0
  128. {evutils-0.3.7 → evutils-0.3.11}/docs/source/_static/event_hexagon_broken.webp +0 -0
  129. {evutils-0.3.7 → evutils-0.3.11}/docs/source/_templates/autoapi/python/module.rst +0 -0
  130. {evutils-0.3.7 → evutils-0.3.11}/docs/source/benchmarks/README.md +0 -0
  131. {evutils-0.3.7 → evutils-0.3.11}/docs/source/index.rst +0 -0
  132. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/__init__.py +0 -0
  133. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/augment/__init__.py +0 -0
  134. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/dataset/__init__.py +0 -0
  135. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/io/__init__.py +0 -0
  136. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/processing/__init__.py +0 -0
  137. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/repr/__init__.py +0 -0
  138. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/utils/__init__.py +0 -0
  139. {evutils-0.3.7 → evutils-0.3.11}/src/evutils/vis/reconstructor/__init__.py +0 -0
  140. /evutils-0.3.7/tests/io/test_bin.py → /evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/__init__.py +0 -0
  141. /evutils-0.3.7/tests/io/test_dat.py → /evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/data/.gitignore +0 -0
  142. /evutils-0.3.7/tests/io/test_hdf5.py → /evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/model/__init__.py +0 -0
  143. /evutils-0.3.7/tests/io/test_npz.py → /evutils-0.3.11/src/evutils/vis/reconstructor/rpg_e2vid/options/__init__.py +0 -0
  144. {evutils-0.3.7 → evutils-0.3.11}/tests/io/conftest.py +0 -0
  145. {evutils-0.3.7 → evutils-0.3.11}/tests/io/test_csv.py +0 -0
  146. {evutils-0.3.7 → evutils-0.3.11}/tests/test_random.py +0 -0
  147. {evutils-0.3.7 → evutils-0.3.11}/tests/test_types.py +0 -0
  148. {evutils-0.3.7 → evutils-0.3.11}/tests/test_vis.py +0 -0
@@ -0,0 +1,143 @@
1
+ name: Release & Publish Docs
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch: # manual runs to exercise the multi-OS wheel build without releasing
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write # Required for trusted PyPI publishing and GitHub Pages deployment
11
+ pages: write # Required for GitHub Pages deployment
12
+
13
+ jobs:
14
+ # Gate everything on the test suite -- no wheels/sdist/publish unless it passes.
15
+ test:
16
+ name: Tests
17
+ uses: ./.github/workflows/test.yaml
18
+
19
+ build_wheels:
20
+ name: Wheels (${{ matrix.os }})
21
+ needs: test
22
+ runs-on: ${{ matrix.os }}
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ # linux x86_64 + arm64 | windows amd64 | macOS (arm64 runner builds both
27
+ # arm64 natively and x86_64 via cross-compile -- see CIBW_ARCHS_MACOS).
28
+ os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-14]
29
+ steps:
30
+ - name: Checkout repository
31
+ uses: actions/checkout@v7
32
+ with:
33
+ submodules: recursive
34
+
35
+ - name: Set up Python
36
+ uses: actions/setup-python@v6
37
+ with:
38
+ python-version: "3.12"
39
+
40
+ - name: Build wheels (cibuildwheel)
41
+ run: pipx run cibuildwheel --output-dir wheelhouse
42
+ env:
43
+ # requires-python (>=3.10) limits the CPython versions built. Skip
44
+ # musllinux, the 32-bit Windows build (win32 has no SIMD path and no
45
+ # numeric-stack wheels), and the free-threaded (cp*t) builds -> standard
46
+ # 64-bit CPython 3.10-3.14 manylinux/macos/win wheels.
47
+ CIBW_SKIP: "*musllinux* *-win32 cp3*t-*"
48
+ # Build both macOS arches on the arm64 runner (x86_64 is cross-compiled;
49
+ # its wheels can't be test-run here, which is fine -- we don't run tests
50
+ # in cibuildwheel). Ignored on Linux/Windows.
51
+ CIBW_ARCHS_MACOS: "x86_64 arm64"
52
+
53
+ - name: Upload wheels
54
+ uses: actions/upload-artifact@v7
55
+ with:
56
+ name: dist-${{ matrix.os }}
57
+ path: wheelhouse/*.whl
58
+
59
+ build_sdist:
60
+ name: Source distribution
61
+ needs: test
62
+ runs-on: ubuntu-latest
63
+ steps:
64
+ - name: Checkout repository
65
+ uses: actions/checkout@v7
66
+ with:
67
+ submodules: recursive
68
+
69
+ - name: Set up Python
70
+ uses: actions/setup-python@v6
71
+ with:
72
+ python-version: "3.12"
73
+
74
+ - name: Build sdist
75
+ run: pipx run build --sdist
76
+
77
+ - name: Upload sdist
78
+ uses: actions/upload-artifact@v7
79
+ with:
80
+ name: dist-sdist
81
+ path: dist/*.tar.gz
82
+
83
+ publish_pypi:
84
+ name: Publish to PyPI
85
+ needs: [build_wheels, build_sdist]
86
+ runs-on: ubuntu-latest
87
+ if: github.event_name == 'release'
88
+ steps:
89
+ - name: Download all distributions
90
+ uses: actions/download-artifact@v4
91
+ with:
92
+ path: dist
93
+ pattern: dist-*
94
+ merge-multiple: true
95
+
96
+ - name: Publish to PyPI
97
+ uses: pypa/gh-action-pypi-publish@release/v1
98
+
99
+ build_and_publish_docs:
100
+ name: Build & Publish Sphinx Docs
101
+ needs: test
102
+ runs-on: ubuntu-latest
103
+ if: github.event_name == 'release'
104
+ # deploy-pages@v4 requires the deploying job to target the github-pages environment.
105
+ environment:
106
+ name: github-pages
107
+ url: ${{ steps.deployment.outputs.page_url }}
108
+ steps:
109
+ - name: Checkout repository
110
+ uses: actions/checkout@v7
111
+ with:
112
+ submodules: recursive
113
+
114
+ - name: Install uv
115
+ uses: astral-sh/setup-uv@v8.2.0
116
+ with:
117
+ enable-cache: true
118
+
119
+ - name: Set up Python
120
+ uses: actions/setup-python@v6
121
+ with:
122
+ python-version: "3.12"
123
+
124
+ - name: Install package and docs dependencies
125
+ # uv compiles the C extension locally and installs dependencies instantly
126
+ run: uv pip install --system .[docs]
127
+
128
+ - name: Build Sphinx HTML
129
+ run: |
130
+ cd docs
131
+ make html
132
+
133
+ - name: Setup GitHub Pages
134
+ uses: actions/configure-pages@v6
135
+
136
+ - name: Upload artifact
137
+ uses: actions/upload-pages-artifact@v5
138
+ with:
139
+ path: 'docs/build/html'
140
+
141
+ - name: Deploy to GitHub Pages
142
+ id: deployment
143
+ uses: actions/deploy-pages@v5
@@ -5,6 +5,7 @@ on:
5
5
  branches: [main, feature/*, fix/*]
6
6
  pull_request:
7
7
  branches: [main, feature/*, fix/*]
8
+ workflow_call: # allow other workflows (e.g. release) to gate on the tests
8
9
 
9
10
  jobs:
10
11
  test:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: evutils
3
- Version: 0.3.7
3
+ Version: 0.3.11
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,17 +30,17 @@ 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
33
34
  Provides-Extra: pandas
34
35
  Requires-Dist: pandas>=2.3.3; extra == "pandas"
35
- Provides-Extra: numba
36
- Requires-Dist: numba>=0.57; extra == "numba"
37
36
  Provides-Extra: torch
38
37
  Requires-Dist: torch>=2.0; extra == "torch"
39
38
  Provides-Extra: hdf5
40
39
  Requires-Dist: hdf5plugin==6.0.0; extra == "hdf5"
41
- Requires-Dist: h5py==3.16.0; extra == "hdf5"
40
+ Requires-Dist: h5py>=3.16.0; extra == "hdf5"
42
41
  Provides-Extra: vis
43
42
  Requires-Dist: matplotlib>=3.1; extra == "vis"
43
+ Requires-Dist: open3d>=0.19.0; python_version < "3.13" and extra == "vis"
44
44
  Requires-Dist: scipy>=1.15.3; extra == "vis"
45
45
  Requires-Dist: tqdm>=4.41; extra == "vis"
46
46
  Provides-Extra: test
@@ -48,7 +48,8 @@ Requires-Dist: pytest>=9.0.2; extra == "test"
48
48
  Requires-Dist: pytest-benchmark>=5.2.3; extra == "test"
49
49
  Provides-Extra: compare
50
50
  Requires-Dist: expelliarmus>=1.1.0; extra == "compare"
51
- Requires-Dist: evlib; extra == "compare"
51
+ Requires-Dist: evlib>=0.12.0; python_version >= "3.11" and extra == "compare"
52
+ Requires-Dist: evlib>=0.8.0; python_version < "3.11" and extra == "compare"
52
53
  Provides-Extra: docs
53
54
  Requires-Dist: sphinx>=8.0.0; extra == "docs"
54
55
  Requires-Dist: sphinx-autoapi>=3.6.1; extra == "docs"
@@ -58,7 +59,7 @@ Requires-Dist: sphinx_book_theme>=1.0.0; extra == "docs"
58
59
  Requires-Dist: pydata_sphinx_theme>=0.19.0; extra == "docs"
59
60
  Requires-Dist: myst-parser>=4.0.0; extra == "docs"
60
61
  Provides-Extra: all
61
- Requires-Dist: evutils[hdf5,numba,pandas,torch,vis]; extra == "all"
62
+ Requires-Dist: evutils[hdf5,pandas,torch,vis]; extra == "all"
62
63
  Provides-Extra: dev
63
64
  Requires-Dist: evutils[all,docs,test]; extra == "dev"
64
65
  Description-Content-Type: text/markdown
@@ -83,10 +84,12 @@ This project draws inspiration from several excellent libraries in the event-bas
83
84
 
84
85
 
85
86
  ## Installation
86
- We recommend installing `evutils` using `uv`.
87
+ We recommend installing `evutils` using `uv`.
87
88
  ### From PyPi
88
89
  ```bash
89
- uv add evutils[all]
90
+ uv add evutils # Basic library
91
+ uv add evutils[all] # All groups (pandas, numba, torch, hdf5, etc..)
92
+ uv add evutils[dev] # Dev group
90
93
  ```
91
94
 
92
95
  ### From Git
@@ -187,26 +190,6 @@ uv run pytest benchmarks/ --benchmark-group-by=param:fmt # compare libraries
187
190
 
188
191
  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
192
 
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
-
202
- | Format | evutils |
203
- | ------ | ------: |
204
- | EVT2 | 146 |
205
- | EVT2.1 | 110 |
206
- | EVT3 | 74 |
207
-
208
- (`–` = format not supported by that library.)
209
-
210
193
 
211
194
  ## Acknowledgements
212
195
 
@@ -18,10 +18,12 @@ This project draws inspiration from several excellent libraries in the event-bas
18
18
 
19
19
 
20
20
  ## Installation
21
- We recommend installing `evutils` using `uv`.
21
+ We recommend installing `evutils` using `uv`.
22
22
  ### From PyPi
23
23
  ```bash
24
- uv add evutils[all]
24
+ uv add evutils # Basic library
25
+ uv add evutils[all] # All groups (pandas, numba, torch, hdf5, etc..)
26
+ uv add evutils[dev] # Dev group
25
27
  ```
26
28
 
27
29
  ### From Git
@@ -122,26 +124,6 @@ uv run pytest benchmarks/ --benchmark-group-by=param:fmt # compare libraries
122
124
 
123
125
  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
126
 
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
-
137
- | Format | evutils |
138
- | ------ | ------: |
139
- | EVT2 | 146 |
140
- | EVT2.1 | 110 |
141
- | EVT3 | 74 |
142
-
143
- (`–` = format not supported by that library.)
144
-
145
127
 
146
128
  ## Acknowledgements
147
129
 
@@ -0,0 +1,131 @@
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 (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_fixed_formats.py` | `evutils` read/write for DAT and AER (+ expelliarmus on DAT read) |
46
+ | `test_compare.py` | third-party readers from `readers.py` (auto-skip if not installed) |
47
+ | `readers.py` | adapter registry — one entry per external library |
48
+
49
+ *Note: DAT reuses the shared EVT3 reference events (its 14-bit coords fit 1280×720). AER is 9-bit and timestamp-less (GenX320-class), so it uses a separate small synthetic fixture.*
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
+ Persist the downloaded recording across runs (otherwise `--rm` discards the pytest cache and it re-downloads every time):
102
+
103
+ ```bash
104
+ docker run --rm -v evutils-cache:/work/.pytest_cache evutils-openeb
105
+ ```
106
+
107
+ Run only the OpenEB comparison (skip the `evutils` rows):
108
+
109
+ ```bash
110
+ docker run --rm evutils-openeb \
111
+ pytest benchmarks/test_compare.py --benchmark-group-by=param:fmt -q
112
+ ```
113
+
114
+ Drop into a shell to debug the build/run:
115
+
116
+ ```bash
117
+ docker run --rm -it evutils-openeb bash
118
+ ```
119
+
120
+ Build a different OpenEB release if the default fails to build:
121
+
122
+ ```bash
123
+ docker build -t evutils-openeb --build-arg OPENEB_VERSION=5.0.0 \
124
+ -f benchmarks/docker/Dockerfile.openeb .
125
+ ```
126
+
127
+ ### Caveats
128
+
129
+ - 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.
130
+ - The first pass is slow: it compiles OpenEB (`-j$(nproc)`) and downloads the reference recording on first run.
131
+ - A repo-root `.dockerignore` keeps the build context small (excludes `.venv`, `build/`, `.git`, `data/`, `*.raw`, etc.).
@@ -0,0 +1,94 @@
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
+
14
+ from evutils.io import EventReader
15
+
16
+ sys.path.append(str(Path(__file__).parent.parent / "tests"))
17
+ from conftest_utils import EventFile, download_and_extract_gdrive, load_event_files
18
+
19
+ #: Cap on how many events are held in memory for the write benchmarks. Large
20
+ #: enough for a stable throughput measurement, small enough to stay light.
21
+ N_REFERENCE = 5_000_000
22
+
23
+
24
+ @pytest.fixture(scope='session')
25
+ def real_event_files(request, benchmark_dataset):
26
+ """Real Prophesee recordings (downloaded + cached on first use).
27
+
28
+ Returns ``{format: [EventFile(path, count)]}``.
29
+ """
30
+ if benchmark_dataset == "large":
31
+ temp_dir = request.config.cache.mkdir("event_files_huge")
32
+ file_id = "1QPuilR1VD0rKyhVOlu1Y-HtkO4ZBc2Cz"
33
+
34
+ json_files = list(temp_dir.glob("*.json"))
35
+ if not json_files:
36
+ download_and_extract_gdrive(file_id, temp_dir, "huge.tar.zst")
37
+
38
+ return load_event_files(temp_dir)
39
+ else:
40
+ temp_dir = request.config.cache.mkdir("event_files")
41
+ file_id = "1uhOsWbp2o3CktsHrFkzGCNFbx0bQLsct"
42
+ filenames = {
43
+ 'evt3': "hand_evt3.raw",
44
+ 'evt21': "hand_evt21.raw",
45
+ 'evt2': "hand_evt2.raw",
46
+ }
47
+ paths = {fmt: temp_dir / name for fmt, name in filenames.items()}
48
+
49
+ for key, path in paths.items():
50
+ if not path.exists():
51
+ download_and_extract_gdrive(file_id, temp_dir, "hand.tar.zst")
52
+ break
53
+
54
+ return load_event_files(temp_dir, paths)
55
+
56
+
57
+ @pytest.fixture(scope='session')
58
+ def reference_events(real_event_files):
59
+ """First ``N_REFERENCE`` events of the real EVT3 recording as an AoS array.
60
+
61
+ Decoded once and reused as the payload for every write benchmark, so the
62
+ write benchmarks measure encoding cost only (not decoding).
63
+ """
64
+ parts = []
65
+ total = 0
66
+ if 'evt3' not in real_event_files or not real_event_files['evt3']:
67
+ pytest.skip("No evt3 files available for reference events")
68
+ # Use the hand_evt3.raw file for reference events
69
+ ef = next((f for f in real_event_files['evt3'] if 'hand' in f.path.name), real_event_files['evt3'][0])
70
+ with EventReader(ef.path, n_events=1_000_000) as reader:
71
+ for chunk in reader:
72
+ parts.append(chunk.to_aos())
73
+ total += len(chunk)
74
+ if total >= N_REFERENCE:
75
+ break
76
+ return np.concatenate(parts)[:N_REFERENCE]
77
+
78
+
79
+ def pytest_addoption(parser):
80
+ parser.addoption(
81
+ "--rounds", action="store", default=4, type=int, help="Number of benchmark rounds"
82
+ )
83
+ parser.addoption(
84
+ "--dataset", action="store", default="small", choices=["small", "large"],
85
+ help="Dataset size to use for benchmarks ('small' or 'large')"
86
+ )
87
+
88
+ @pytest.fixture(scope="session")
89
+ def benchmark_rounds(request):
90
+ return request.config.getoption("--rounds")
91
+
92
+ @pytest.fixture(scope="session")
93
+ def benchmark_dataset(request):
94
+ return request.config.getoption("--dataset")
@@ -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"]
@@ -42,7 +42,12 @@ def _count(obj) -> int:
42
42
  def _read_expelliarmus(path: str, fmt: str) -> int:
43
43
  # https://github.com/open-neuromorphic/expelliarmus (evt2, evt3, dat)
44
44
  from expelliarmus import Wizard
45
- return _count(Wizard(encoding=fmt).read(str(path)))
45
+ wiz = Wizard(encoding=fmt)
46
+ wiz.set_file(str(path))
47
+ total = 0
48
+ for chunk in wiz.read_chunk():
49
+ total += len(chunk)
50
+ return total
46
51
 
47
52
 
48
53
  # NOTE: tonic (https://github.com/neuromorphs/tonic) has no standalone EVT/.raw
@@ -53,11 +58,17 @@ def _read_expelliarmus(path: str, fmt: str) -> int:
53
58
 
54
59
  def _read_evlib(path: str, fmt: str) -> int:
55
60
  # https://github.com/tallamjr/evlib (Rust-backed). Auto-detects the format;
56
- # load_events() returns a polars LazyFrame, so collect() to force decoding.
61
+ # load_events() returns a polars LazyFrame. To avoid OOM on huge files,
62
+ # we use an aggregation (pl.len()) and stream it using Polars streaming engine.
57
63
  import evlib
64
+ import polars as pl
58
65
  df = evlib.load_events(str(path))
59
66
  if hasattr(df, "collect"):
60
- df = df.collect()
67
+ try:
68
+ return df.select(pl.len()).collect(engine='streaming').item()
69
+ except TypeError:
70
+ # Fallback for older Polars versions
71
+ return df.select(pl.len()).collect(streaming=True).item()
61
72
  return _count(df)
62
73
 
63
74
 
@@ -21,16 +21,19 @@ from readers import ALL_FORMATS, READERS
21
21
 
22
22
  @pytest.mark.parametrize("fmt", ALL_FORMATS)
23
23
  @pytest.mark.parametrize("reader", READERS, ids=lambda r: r.name)
24
- def test_read_compare(benchmark, real_event_files, reader, fmt):
24
+ def test_read_compare(benchmark, benchmark_rounds, real_event_files, reader, fmt):
25
25
  if fmt not in reader.formats:
26
26
  pytest.skip(f"{reader.name} does not support {fmt}")
27
27
 
28
- ef = real_event_files[fmt]
28
+ benchmark.group = f"read-{fmt}"
29
+ if fmt not in real_event_files or not real_event_files[fmt]:
30
+ pytest.skip(f"No files for format {fmt}")
31
+ ef = next((f for f in real_event_files[fmt] if 'hand' in f.path.name), real_event_files[fmt][0])
29
32
 
30
33
  try:
31
34
  n = benchmark.pedantic(
32
35
  lambda: reader.read(ef.path, fmt),
33
- rounds=3, iterations=1, warmup_rounds=1,
36
+ rounds=benchmark_rounds, iterations=1, warmup_rounds=1,
34
37
  )
35
38
  except ImportError as exc:
36
39
  pytest.skip(f"{reader.name} not available: {exc}")