Analyzing Event Data with Python
Prerequisite: first use evt2_to_csv or MultiVision Studio to export a .raw recording to .csv (x, y, polarity, timestamp, see Data Formats). The examples on this page only read CSV and do not call any SDK — install pandas / numpy / matplotlib and you can run them.
No camera?
Grab a .raw from Downloads — Sample recordings, export it to CSV, and run the code below directly.
1. Read and basic statistics
import pandas as pd
df = pd.read_csv("events.csv") # 列:x, y, polarity, timestamp(微秒)
print(df.head())
duration_us = df["timestamp"].iloc[-1] - df["timestamp"].iloc[0]
eps = len(df) / (duration_us / 1e6)
print(f"录制时长:{duration_us/1e6:.3f} s")
print(f"事件率 EPS:{eps:,.0f} events/s")
print(f"ON 事件占比:{(df['polarity']==1).mean():.1%}")2. Accumulate events into a frame with numpy (simple accumulation)
import numpy as np
H, W = 608, 768 # 按你的传感器分辨率调整(CF-NRS1 EVS 为 768×608)
t0 = df["timestamp"].min()
window_us = 50_000 # 50 ms 累积窗
df_win = df[(df["timestamp"] - t0) < window_us]
frame = np.zeros((H, W), dtype=np.int16)
pol = np.where(df_win["polarity"].to_numpy() == 1, 1, -1)
np.add.at(frame, (df_win["y"].to_numpy(), df_win["x"].to_numpy()), pol)Adjust
window_usto observe the effect of accumulation time on the picture: too small and the picture is sparse, too large and motion smears.
3. Visualization
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.imshow(frame, cmap="bwr", vmin=-5, vmax=5)
plt.title(f"accumulation {window_us/1000:.0f} ms")
plt.axis("off")
plt.colorbar(label="事件累加(红=ON,蓝=OFF)")
plt.savefig("frame.png", dpi=120)
plt.show()You can also plot an event scatter (XYT) to observe the motion trajectory:
sub = df_win.sample(min(len(df_win), 20000)) # 下采样避免点太密
plt.figure(figsize=(6, 6))
plt.scatter(sub["x"], sub["y"], c=sub["polarity"], s=1, cmap="bwr")
plt.gca().invert_yaxis()
plt.xlabel("x"); plt.ylabel("y"); plt.title("事件散点(XY)")
plt.savefig("scatter.png", dpi=120)
plt.show()Advanced: capture directly from the camera in real time
The v2.0 Python bindings (a single hv_toolkit module) already support capturing directly from the camera in real time; the same API covers both USB (x86_64) and MIPI HVS (S100/RK3588). Below, the accumulate/statistics ideas from the offline analysis above are wired up to the live stream: decode while capturing, accumulate into frames on the fly.
Build the bindings first
The Python bindings are disabled by default; build with --python: ./run.sh --python build x86_64 (USB) or ./run.sh --python build s100 (MIPI HVS). For the full steps see Your First Python Program; for the interface see Python API.
USB (x86_64)
import numpy as np
import hv_toolkit as hv
cfg = hv.DeviceConfig()
cfg.backend = hv.Backend.Usb
cfg.vendor_id = 0x1d6b
cfg.product_id = 0x0105
cam = hv.Camera()
cam.init(cfg)
cam.start_stream()
dec = hv.Evt2Decoder() # USB uses EVT2
H, W = 608, 768
f = hv.Frame()
window = [] # recent-window event buffer
for _ in range(100):
if not cam.get_frame(f, 1000):
continue
ev = dec.decode(bytes(f.evs)) # → ndarray, fields x/y/t/polarity
window.append(ev)
# Accumulate into a frame just like the offline analysis
all_ev = np.concatenate(window)
frame = np.zeros((H, W), dtype=np.int16)
pol = np.where(all_ev['polarity'], 1, -1)
np.add.at(frame, (all_ev['y'], all_ev['x']), pol)
cam.stop_stream()
cam.destroy()MIPI HVS (S100)
Only the backend config and the decoder change — Camera/Frame/get_frame are exactly the same:
cfg = hv.DeviceConfig()
cfg.backend = hv.Backend.MipiHvs
cfg.device_node = "/dev/video0"
cfg.sensor_index = 0
cfg.i2c_bus = 1
cam = hv.Camera(); cam.init(cfg); cam.start_stream()
dec = hv.MipiRaw8Decoder() # MIPI HVS Frame.evs is a RAW8 subframe stream; you must use this decoder
f = hv.Frame()
while cam.get_frame(f, 1000):
ev = dec.decode(bytes(f.evs))
aps_bytes = f.aps.nbytes # APS is usually NV12
print(f"{len(ev)} events, aps={aps_bytes} bytes, fmt={f.format}")
cam.stop_stream(); cam.destroy()For on-board deployment (
LD_LIBRARY_PATH/PYTHONPATHpointing to/home/sunrise/build) see Python API → Deploy to the S100 board. Once you have live events, the visualization code is exactly the same as the offline part above (matplotlibaccumulation frames / scatter plots).
