pulse2percept.stimuli.FrequencyEncoder

class pulse2percept.stimuli.FrequencyEncoder(implant=None, freq_range=(0, 300), amp=50, **kwargs)[source]

Encode gray levels as pulse train frequencies

Every electrode emits pulses of the same fixed amplitude, and the gray level of the pixel it sees sets how often they come.

Important

Frequency modulation is far more expensive to simulate than amplitude modulation, because electrodes pulsing at different rates do not pulse at the same times: the stimulus needs a time point wherever any electrode’s pulse has an edge, rather than the handful of time points that amplitude modulation shares between all of them.

clock is the lever that cuts that down, and it is physically motivated: real stimulators have a time base. Encoding a 94-frame clip for Argus II at frequencies in (0, 300] Hz:

setting

time points

(amplitude modulation)

442

no quantization

143,771

clock=1

21,505

clock=2

10,893

n_levels=8

127,327

clock=1, n_levels=8

20,917

clock is not free, though: it buys those time points with frequency resolution, and it spends it at the top of the range where the periods are shortest. Against freq_range=(0, 300), clock=1 delivers the brightest pixels at 250 Hz rather than 300, and clock=2 at 200 Hz. Pick it against the fastest train you actually need.

n_levels is a much weaker lever here than the numbers above might suggest, and only worth reaching for once clock is set. Because the pulse clock keeps its phase across frames, two electrodes quantized onto the same gray level still pulse at different times unless their whole history matches; quantizing gray levels no longer collapses them onto a shared schedule the way it would if every frame restarted the train.

A raster cuts the cost too, and for the same reason a clock does: it confines every onset to the raster grid.

Added in version 0.10.0.

Parameters:
  • implant (Implant, optional) – The implant to encode for. See PulseEncoder.

  • freq_range ((min_freq, max_freq), optional) –

    Range of pulse train frequencies (Hz). A gray level of 0 maps onto min_freq and a gray level of 1 onto max_freq. A frequency of 0 means no pulse at all.

    Note

    Realizable frequencies are quantized by clock, and, when a raster is in play, onto the raster sweep – which under frequency modulation is the usual case, since the electrodes are by construction on differing rates. Every period becomes a whole number of sweeps, so the realizable rates are 1000 / (m * sweep) Hz.

    How coarse that grid is depends on how the sweep was set. With group_dur=None the sweep is the shortest period asked for, so the fastest electrode keeps its rate and pulses once per sweep while slower ones pulse every m-th. With an explicit group_dur the sweep is n_groups * group_dur and unrelated to any requested rate, so even the fastest electrode is generally rounded: against a six-group 1 ms sweep, a requested 100 Hz (10 ms) is delivered as 83.3 Hz (12 ms, two sweeps).

    Quantizing onto the sweep always rounds the period up, so an electrode is never driven faster than it was asked for: against a 10 ms sweep, 67 Hz comes back as 50 Hz rather than 100 Hz. Rounding to the nearest sweep instead would deliver up to twice the charge the caller asked for. Shorten group_dur for a finer grid.

  • amp (float, optional) – Pulse amplitude (uA), the same for every electrode.

  • phase_dur – See PulseEncoder.

  • interphase_dur – See PulseEncoder.

  • cathodic_first – See PulseEncoder.

  • pulse – See PulseEncoder.

  • clock – See PulseEncoder.

  • n_levels – See PulseEncoder.

  • frame_dur – See PulseEncoder.

  • stretch – See PulseEncoder.

Notes

  • Arguments may be given as plain numbers in the units documented above, or as unitful quantities (e.g. 0.05 * mA, 460 * us, 0.02 * kHz), which are converted to those units. See pulse2percept.units.

Examples

Encode a movie for Argus II at 50 uA, mapping gray levels onto 0-300 Hz on a 1 ms stimulator clock. A 300 Hz period is 3.3 ms, which Argus II’s own six-group 2 ms raster sweep does not fit into, so this device drives every electrode at once:

>>> import numpy as np
>>> import pulse2percept as p2p
>>> video = p2p.stimuli.VideoStimulus(np.random.rand(16, 20, 30),
...                                   metadata={'fps': 30})
>>> implant = p2p.implants.retina.ArgusII(raster=None)
>>> encoder = p2p.stimuli.FrequencyEncoder(implant, freq_range=(0, 300),
...                                        amp=50, clock=1)
>>> stim = encoder.encode(video)
__init__(implant=None, freq_range=(0, 300), amp=50, **kwargs)[source]

Methods

__init__([implant, freq_range, amp])

encode(source)

Encode an image or a video as a train of electrical pulses

Attributes

freq_range

amp

amp_unit

Unit of amplitudes returned by _modulate

cathodic_first

clock

frame_dur

implant

The implant this encoder is bound to, or None (read-only)

interphase_dur

n_levels

phase_dur

pulse

stretch

amp_unit = uA[source]

Unit of amplitudes returned by _modulate

encode(source)[source]

Encode an image or a video as a train of electrical pulses

Parameters:

source (Stimulus) – The image or video to encode. Gray levels are expected in [0, 1], which is what ImageStimulus and VideoStimulus produce. It must be dimensionless: this method is the boundary at which a picture becomes stimulation, so an electrical stimulus is not a valid source for it.

Returns:

stim – The encoded stimulus, ready for the implant to deliver. Its amplitudes are in microamps, or in threshold multiples (xTh) if the encoder’s amplitude parameters were, and its time axis is in milliseconds, whatever units the encoder’s own parameters were given in.

Return type:

Stimulus

Raises:

DimensionMismatchError – If source is not dimensionless.

property implant

The implant this encoder is bound to, or None (read-only)