Skip to content

Commit

Permalink
change Timesurface transform parameters and speed it up considerably
Browse files Browse the repository at this point in the history
  • Loading branch information
biphasic committed Jul 2, 2023
1 parent 4763042 commit 87b88a9
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 91 deletions.
33 changes: 16 additions & 17 deletions test/test_representations.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,29 +201,28 @@ def test_representation_image():


@pytest.mark.parametrize(
"surface_dimensions, tau, delta_t", [((15, 15), 100, 0), ((3, 3), 10, 1e4), (None, 1e4, 1e5)]
"sensor_size, dt, tau,",
[
((40, 15, 2), 10000, 10000),
((10, 20, 2), 20000, 1000),
((30, 30, 2), 30000, 3000),
],
)
def test_representation_time_surface(surface_dimensions, tau, delta_t):
orig_events, sensor_size = create_random_input(n_events=1000)

transform = transforms.ToTimesurface(
sensor_size=sensor_size, surface_dimensions=surface_dimensions, tau=tau, delta_t=delta_t
def test_representation_time_surface(sensor_size, dt, tau):
orig_events, sensor_size = create_random_input(
sensor_size=sensor_size, n_events=10000
)

transform = transforms.ToTimesurface(sensor_size=sensor_size, dt=dt, tau=tau)

surfaces = transform(orig_events)

if delta_t == 0:
assert surfaces.shape[0] == len(orig_events)
else:
duration = orig_events["t"][-1] - orig_events["t"][0]
assert surfaces.shape[0] == duration // delta_t

duration = orig_events["t"][-1] - orig_events["t"][0]
assert surfaces.shape[0] == duration // dt

assert surfaces.shape[1] == 2
if surface_dimensions:
assert surfaces.shape[2:] == surface_dimensions
else:
assert surfaces.shape[2] == sensor_size[1]
assert surfaces.shape[3] == sensor_size[0]
assert surfaces.shape[2] == sensor_size[1]
assert surfaces.shape[3] == sensor_size[0]
assert surfaces is not orig_events


Expand Down
102 changes: 35 additions & 67 deletions tonic/functional/to_timesurface.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,51 @@
from typing import Tuple

import numpy as np

from tonic.slicers import slice_events_by_time


def to_timesurface_numpy(
events, sensor_size, surface_dimensions=None, tau=5e3, delta_t=0, decay="lin"
events,
sensor_size: Tuple[int, int, int],
dt: float,
tau: float,
overlap: int = 0,
include_incomplete: bool = False,
):
"""Representation that creates timesurfaces for each event in the recording. Modeled after the
paper Lagorce et al. 2016, Hots: a hierarchy of event-based time-surfaces for pattern
recognition https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7508476.
recognition https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=7508476. Unlike the paper,
surfaces are always generated across the whole sensor, not just around the event.
Parameters:
surface_dimensions (int, int): width does not have to be equal to height, however both numbers have to be odd.
if surface_dimensions is None: the time surface is defined globally, on the whole sensor grid.
sensor_size: x/y/p dimensions of the sensor
dt: time interval at which the time-surfaces are accumulated
tau (float): time constant to decay events around occuring event with.
delta_t (float): the interval at which the time-surfaces are accumulated, if set 0 number of time-surfaces will
equal to the number of events. (defaults to 0)
decay (str): can be either 'lin' or 'exp', corresponding to linear or exponential decay.
Returns:
array of timesurfaces with dimensions (n_events//delta_t,w,h) or (n_events//delta_t,p,w,h)
array of timesurfaces with dimensions (n_events//dt, p, h , w)
"""

assert delta_t >= 0, print("Parameter delta_t cannot be negative.")

if delta_t > 0:
duration = events['t'][-1] - events['t'][0]
n_surfaces = int(duration // delta_t)
last_accumulated = events['t'][0]
last_event_timestamp = events['t'][0]
accumulated_surface_index = 0
else:
n_surfaces = len(events)

if surface_dimensions:
assert len(surface_dimensions) == 2
assert surface_dimensions[0] % 2 == 1 and surface_dimensions[1] % 2 == 1
radius_x = surface_dimensions[0] // 2
radius_y = surface_dimensions[1] // 2
else:
radius_x = 0
radius_y = 0
surface_dimensions = sensor_size

assert "x" and "y" and "t" and "p" in events.dtype.names
assert dt >= 0, print("Parameter delta_t cannot be negative.")

timestamp_memory = np.zeros(
(sensor_size[2], sensor_size[1] + radius_y * 2, sensor_size[0] + radius_x * 2)
event_slices = slice_events_by_time(
events, time_window=dt, overlap=overlap, include_incomplete=include_incomplete
)
timestamp_memory -= tau * 3 + 1
all_surfaces = np.zeros(
(n_surfaces, sensor_size[2], surface_dimensions[1], surface_dimensions[0])
)

for index, event in enumerate(events):

x = int(event["x"])
y = int(event["y"])
timestamp_memory[int(event["p"]), y + radius_y, x + radius_x] = event["t"]
if radius_x > 0 and radius_y > 0:
timestamp_context = (
timestamp_memory[
:, y : y + surface_dimensions[1], x : x + surface_dimensions[0]
]
- event["t"]
)
else:
timestamp_context = timestamp_memory - event["t"]

if decay == "lin":
timesurface = timestamp_context / (3 * tau) + 1
timesurface[timesurface < 0] = 0
elif decay == "exp":
timesurface = np.exp(timestamp_context / tau)

if delta_t == 0:
all_surfaces[index, :, :, :] = timesurface
else:
last_event_timestamp = event['t']
if float(last_event_timestamp) - float(last_accumulated) > delta_t:
all_surfaces[accumulated_surface_index, :, :, :] = timesurface
accumulated_surface_index += 1
last_accumulated = event['t']
return all_surfaces
memory = np.zeros((sensor_size[::-1]), dtype=int)
all_surfaces = []
x_index = event_slices[0].dtype.names.index("x")
y_index = event_slices[0].dtype.names.index("y")
p_index = event_slices[0].dtype.names.index("p")
t_index = event_slices[0].dtype.names.index("t")
start_t = event_slices[0][0][t_index]
for i, slice in enumerate(event_slices):
# structured to unstructured in order to access the indices
slice = slice.view((int, len(slice.dtype.names)))
indices = slice[:, [p_index, y_index, x_index]].T
timestamps = slice[:, t_index]
memory[tuple(indices)] = timestamps
diff = -((i + 1) * dt + start_t - memory)
surf = np.exp(diff / tau)
all_surfaces.append(surf)
return np.array(all_surfaces)
10 changes: 3 additions & 7 deletions tonic/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,19 +919,15 @@ class ToTimesurface:
"""

sensor_size: Tuple[int, int, int]
surface_dimensions: Union[None, Tuple[int, int]] = None
tau: float = 5e3
delta_t: float = 0.0
decay: str = "lin"
dt: float
tau: float

def __call__(self, events):
return functional.to_timesurface_numpy(
events=events,
sensor_size=self.sensor_size,
surface_dimensions=self.surface_dimensions,
dt=self.dt,
tau=self.tau,
delta_t=self.delta_t,
decay=self.decay,
)


Expand Down

0 comments on commit 87b88a9

Please sign in to comment.