Coverage for tests/test_repr.py: 100%
120 statements
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-15 09:33 +0000
« prev ^ index » next coverage.py v7.15.1, created at 2026-07-15 09:33 +0000
1import numpy as np
2import pytest
4from evutils.types import Event_dtype
5from evutils.repr import (
6 timesurface,
7 voxel_histogram,
8 histogram,
9 wedge_histogram,
10 frame_diff,
11 frame_rgb,
12 frame_gray,
13 tore
14)
16def create_events(data):
17 events = np.zeros(len(data), dtype=Event_dtype)
18 for i, (t, x, y, p) in enumerate(data):
19 events[i]['t'] = t
20 events[i]['x'] = x
21 events[i]['y'] = y
22 events[i]['p'] = p
23 return events
25def test_timesurface():
26 events = create_events([(100, 10, 20, 1), (200, 15, 25, 0)])
27 ts = timesurface(events, width=100, height=50, tau=100)
28 assert ts.shape == (50, 100)
29 assert ts[20, 10] == pytest.approx(np.exp(-(200 - 100) / 100.0))
30 assert ts[25, 15] == pytest.approx(-1.0)
32def test_timesurface_empty():
33 events = create_events([])
34 ts = timesurface(events, width=100, height=50)
35 assert ts.shape == (50, 100)
36 assert np.all(ts == 0)
38def test_voxel_histogram():
39 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0), (200, 10, 20, 1)])
40 vh = voxel_histogram(events, width=100, height=50, n_bins=2, dt=100)
41 assert vh.shape == (2, 50, 100, 3)
43def test_voxel_histogram_empty():
44 events = create_events([])
45 vh = voxel_histogram(events, width=100, height=50, n_bins=10, dt=1000)
46 assert vh.shape == (10, 50, 100, 3)
47 assert np.all(vh == 0)
49def test_voxel_histogram_less_than_3_events():
50 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0)])
51 vh = voxel_histogram(events, width=100, height=50, n_bins=2, dt=100)
52 assert vh.shape == (2, 50, 100, 3)
53 assert np.all(vh == 0)
55def test_voxel_histogram_exceeds_dt():
56 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0), (250, 10, 20, 1)])
57 with pytest.raises(AssertionError):
58 voxel_histogram(events, width=100, height=50, n_bins=2, dt=100)
60def test_histogram():
61 events = create_events([(100, 10, 20, 1), (200, 15, 25, 0), (250, 10, 20, 1)])
62 h = histogram(events, width=100, height=50, fill=False)
63 assert h.shape == (50, 100, 3)
64 assert h[20, 10, 0] == 2 # Red channel for p=1
65 assert h[25, 15, 2] == 1 # Blue channel for p=0
67def test_histogram_empty():
68 events = create_events([])
69 h = histogram(events, width=100, height=50)
70 assert h.shape == (50, 100, 3)
71 assert np.all(h == 0)
73def test_histogram_fill():
74 events = create_events([(100, 10, 20, 1)])
75 h = histogram(events, width=100, height=50, fill=True)
76 assert h.shape == (50, 100, 3)
77 assert h[20, 10, 0] == 255
79def test_wedge_histogram():
80 events = create_events([(100, 10, 20, 1), (200, 15, 25, 0)])
81 wh = wedge_histogram(events, width=100, height=50, tl=300)
82 assert wh.shape == (50, 100, 3)
84def test_frame_diff():
85 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0), (200, 10, 20, 1)])
86 fd = frame_diff(events, width=100, height=50)
87 assert fd.shape == (50, 100)
88 assert fd[20, 10] == 2
89 assert fd[25, 15] == -1
91def test_frame_diff_empty():
92 events = create_events([])
93 fd = frame_diff(events, width=100, height=50)
94 assert fd.shape == (50, 100)
95 assert np.all(fd == 0)
97def test_frame_rgb():
98 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0)])
99 frgb = frame_rgb(events, width=100, height=50)
100 assert frgb.shape == (50, 100, 3)
101 assert np.array_equal(frgb[20, 10], [255, 0, 0])
102 assert np.array_equal(frgb[25, 15], [0, 0, 255])
103 assert np.array_equal(frgb[0, 0], [0, 0, 0])
105def test_frame_gray():
106 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0)])
107 fg = frame_gray(events, width=100, height=50)
108 assert fg.shape == (50, 100)
109 assert fg[20, 10] == 255
110 assert fg[25, 15] == 0
111 assert fg[0, 0] == 128
113def test_tore():
114 events = create_events([(100, 10, 20, 1), (150, 15, 25, 0), (200, 10, 20, 1)])
115 t = tore(events, width=100, height=50, n_events=2, tau=100)
116 assert t.shape == (50, 100, 2, 2)
117 assert t[20, 10, 1, 1] == pytest.approx(1.0)
118 assert t[20, 10, 0, 1] == pytest.approx(np.exp(-1.0))
120def test_tore_empty():
121 events = create_events([])
122 t = tore(events, width=100, height=50, n_events=4)
123 assert t.shape == (50, 100, 4, 2)
124 assert np.all(t == 0)
127# --- saturation / overflow edge cases (branch coverage) --------------------
129def test_histogram_saturation():
130 """A uint8 histogram bin clips at 255 instead of overflowing."""
131 events = create_events([(i, 10, 20, 1) for i in range(300)]) # 300 hits, one pixel
132 h = histogram(events, width=100, height=50, fill=False)
133 assert h[20, 10, 0] == 255 # capped, not wrapped to 300 % 256
136def test_wedge_histogram_saturation():
137 """A wedge bin saturates: a second selected hit on the same pixel is not
138 added once the bin is already at 255."""
139 events = create_events([(100, 10, 20, 1), (200, 10, 20, 1)])
140 wh = wedge_histogram(events, width=100, height=50, tl=300)
141 assert wh[20, 10, 0] == 255 # single 255, second hit skipped
144def test_tore_fifo_overflow():
145 """With more events than n_events on one pixel/polarity, the oldest fall out
146 of the FIFO; only the most recent n_events survive."""
147 events = create_events([(100, 10, 20, 1), (150, 10, 20, 1), (200, 10, 20, 1)])
148 t = tore(events, width=100, height=50, n_events=2, tau=100)
149 assert t.shape == (50, 100, 2, 2)
150 # newest (t=200) at slot 1, second-newest (t=150) at slot 0; t=100 dropped.
151 assert t[20, 10, 1, 1] == pytest.approx(1.0)
152 assert t[20, 10, 0, 1] == pytest.approx(np.exp(-0.5))
155def test_voxel_histogram_more_windows_than_bins():
156 """Extra time windows beyond n_bins are dropped (the loop breaks). Span
157 0..100 at bin_dt=50 yields 3 windows, one more than n_bins=2."""
158 events = create_events([(0, 10, 20, 1), (50, 11, 21, 0), (100, 12, 22, 1)])
159 vh = voxel_histogram(events, width=100, height=50, n_bins=2, dt=100)
160 assert vh.shape == (2, 50, 100, 3)
163def test_voxel_histogram_fewer_windows_than_bins():
164 """Fewer time windows than n_bins: the loop exhausts normally (no break),
165 leaving the trailing bins zero. Span 20 at bin_dt=1000 is a single window."""
166 events = create_events([(0, 10, 20, 1), (10, 11, 21, 0), (20, 12, 22, 1)])
167 vh = voxel_histogram(events, width=100, height=50, n_bins=10, dt=10000)
168 assert vh.shape == (10, 50, 100, 3)
169 assert np.all(vh[1:] == 0) # only the first window populated