Skip to article frontmatterSkip to article content

4Core concepts

Understanding Faery’s fundamental concepts is essential for effectively processing neuromorphic event data. This section introduces the key abstractions that power Faery’s stream processing architecture.

4.1Event data format

Neuromorphic sensors generate events - discrete signals triggered by changes in the environment. Each event represents a pixel that detected a change at a specific time.

4.1.1Event structure

Every event in Faery contains four pieces of information:

Event = (timestamp, x_coordinate, y_coordinate, polarity)

4.1.2Example events

import numpy as np
import faery

# Create events manually
events = np.array([
    (1000, 320, 240, True),   # Brightness increased at center pixel at t=1ms
    (1500, 321, 240, False),  # Brightness decreased at adjacent pixel at t=1.5ms
    (2000, 319, 241, True),   # Another increase nearby at t=2ms
], dtype=faery.EVENTS_DTYPE)

This sparse representation is highly efficient - only changing pixels generate events, unlike traditional cameras that capture every pixel at fixed intervals.

4.2Stream processing architecture

Faery processes events through streams - sequences of event packets that flow through processing pipelines. Understanding stream types is crucial for building effective data processing workflows.

Stream TypeFinite?Regular?Can .to_array()?Can .to_file() video?
EventsStream
FiniteEventsStreamAfter .regularize()
RegularEventsStream
FiniteRegularEventsStream

Understanding these stream types and their sources enables you to choose the right input and build effective processing pipelines for your specific use case.

4.2.1Stream hierarchy

Stream Type Relationships

Streams are organized into a hierarchy based on two key characteristics:

  1. Finiteness: Whether the stream has a definite end
  2. Regularity: Whether packets arrive at fixed time intervals

4.3Stream types explained

4.3.1Base stream types

EventsStream (Infinite, Irregular)

FiniteEventsStream (Finite, Irregular)

4.3.2Regular stream types

RegularEventsStream (Infinite, Regular)

FiniteRegularEventsStream (Finite, Regular)

4.4Implementation details

All streams in Faery are implemented as a Stream class which iterates over data packets. The data type characterizes what is contained in each packet:

4.4.1Data types

StreamData TypeCharacteristics
EventsStreamnp.ndarraySparse event data represented as timestamps, 2-d coordinates, and polarity bit (t, x, y, p).
FrameStreamFrameDense frame data represented as [timestamp, np.ndarray]](python/faery/frame_stream.py).

4.4.2Stream characteristics

Apart from the data type, streams have characteristics that determine what operations are possible:

Stream TypeDescriptionExamples
InfiniteStreamRequires continuous processing, no definite endReading from camera or UDP source
FiniteStreamCan be processed in a single pass, has definite endReading from file or finite source
RegularStreamSends data at regular intervalsFiltering stream to output at fixed intervals
FiniteRegularStreamBoth finite and regularFiltering finite stream at regular intervals

4.5Stream type transformations

Understanding how operations transform stream types is key to building valid processing pipelines:

import faery

# File input creates FiniteEventsStream
stream = faery.events_stream_from_file("events.aedat4")  # FiniteEventsStream

# Regularization creates FiniteRegularEventsStream
regular_stream = stream.regularize(frequency_hz=30.0)    # FiniteRegularEventsStream

# Rendering creates a FrameStream
rendered_stream = regular_stream.render(decay="exponential", tau="00:00:00.200000", colormap=faery.colormaps.starry_night)  # FiniteRegularFrameStream

# Video output requires a finite frame stream
rendered_stream.to_file("output.mp4")  # ✓ Valid - finite stream can be saved to file

4.5.1Making Infinite Streams Finite

Some operations require finite streams. Use slicing operations to convert:

# This would fail - infinite streams cannot be saved as complete video files
camera_stream = faery.events_stream_from_camera()  # EventsStream (infinite)
camera_stream.regularize(30.0).render(...).to_file("video.mp4")  # ❌ Error - no known end

# Solution: Use time_slice to make it finite
camera_stream.time_slice(0 * faery.s, 10 * faery.s)  # ✓ First 10 seconds
    .regularize(frequency_hz=30.0) \
    .render(decay="exponential", tau="00:00:00.100000", colormap=faery.colormaps.devon) \
    .to_file("10_second_video.mp4")  # ✓ Valid - finite duration

# Or use event_slice for a specific number of events
camera_stream.event_slice(0, 100000)  # ✓ First 100,000 events
    .to_array()  # ✓ Valid - finite number of events

4.6Processing Pipeline Patterns

4.6.1Pattern 1: File Processing

# Finite → Finite Regular → Output
faery.events_stream_from_file("input.aedat4") \
    .regularize(frequency_hz=60.0) \
    .render(decay="exponential", tau="00:00:00.200000", colormap=faery.colormaps.starry_night) \
    .to_file("output.mp4")

4.6.2Pattern 2: Real-time Processing

# Infinite → Finite → Regular → Output
faery.events_stream_from_camera() \
    .time_slice(0 * faery.us, 5 * faery.s) \
    .regularize(frequency_hz=30.0) \
    .render(decay="exponential", tau="00:00:00.100000", colormap=faery.colormaps.devon) \
    .to_file("live.mp4")  # Creates 5-second video file

4.6.3Pattern 3: Analysis Pipeline

# Finite → Analysis
faery.events_stream_from_file("data.es") \
    .remove_off_events() \
    .to_event_rate(window_duration_us=100000) \
    .to_file("event_rates.csv")

4.7Stream Type Transformations

Understanding how operations change stream types:

4.7.1Making Infinite Streams Finite

# ❌ This fails - infinite stream can't create complete video
faery.events_stream_from_camera().regularize(30.0).render(...).to_file("video.mp4")

# ✅ This works - slice first to make finite
faery.events_stream_from_camera().time_slice(0 * faery.s, 5 * faery.s) \
    .regularize(30.0).render(...).to_file("video.mp4")

4.7.2Making Irregular Streams Regular

# File (Finite + Irregular) → regularize() → (Finite + Regular)
faery.events_stream_from_file("data.es").regularize(frequency_hz=60.0)

# Camera (Infinite + Irregular) → regularize() → (Infinite + Regular)
faery.events_stream_from_camera().regularize(frequency_hz=30.0)

4.8Stream State and Memory

Faery streams are designed for memory efficiency:

4.8.1Streaming Processing

# Events are processed in small packets - memory usage stays constant
large_file_stream = faery.events_stream_from_file("10GB_events.aedat4")
processed = large_file_stream.regularize(frequency_hz=30.0)  # Still memory-efficient

4.8.2Finite Collection

# Only when explicitly collecting all data does memory usage grow
all_events = large_file_stream.to_array()  # Loads entire file into memory

4.9Common Patterns by Stream Type

4.9.1Finite Streams (Files, Arrays, stdin)

# Pattern: Input → Filter → Regular → Output
source.filter_operation() \
      .regularize(frequency_hz) \
      .render(...) \
      .to_file("output.mp4")  # ✅ Always works

4.9.2Infinite Streams (Cameras, UDP)

# Pattern: Input → Slice → Regular → Output
source.time_slice(start, end) \
      .regularize(frequency_hz) \
      .render(...) \
      .to_file("output.mp4")  # ✅ Works after slicing

# Pattern: Input → Process → Stream
source.filter_operation() \
      .to_udp(address)  # ✅ Stream-to-stream always works

4.10Practical Guidelines

4.10.1Choose the Right Input

4.10.2Design Processing Pipelines

  1. Start with your input type - determines initial stream type
  2. Apply filtering early - reduce data volume for efficiency
  3. Regularize when needed - required for video output and real-time processing
  4. Choose appropriate output - match output requirements to stream type

4.10.3Debug Stream Issues

# Check stream type and properties
stream = faery.events_stream_from_file("data.es")
print(f"Dimensions: {stream.dimensions()}")
print(f"Stream type: {type(stream)}")

# Preview first few packets
for i, packet in enumerate(stream):
    print(f"Packet {i}: {len(packet)} events")
    if i >= 2:  # Just show first 3 packets
        break

Understanding these concepts enables you to build efficient, type-safe processing pipelines that leverage Faery’s full capabilities while avoiding common pitfalls.