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.
clockis 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=121,505
clock=210,893
n_levels=8127,327
clock=1, n_levels=820,917
clockis 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. Againstfreq_range=(0, 300),clock=1delivers the brightest pixels at 250 Hz rather than 300, andclock=2at 200 Hz. Pick it against the fastest train you actually need.n_levelsis a much weaker lever here than the numbers above might suggest, and only worth reaching for onceclockis 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. SeePulseEncoder.freq_range ((min_freq, max_freq), optional) –
Range of pulse train frequencies (Hz). A gray level of 0 maps onto
min_freqand a gray level of 1 ontomax_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 are1000 / (m * sweep)Hz.How coarse that grid is depends on how the sweep was set. With
group_dur=Nonethe 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 explicitgroup_durthe sweep isn_groups * group_durand 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_durfor 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. Seepulse2percept.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_rangeampUnit of amplitudes returned by _modulate
cathodic_firstclockframe_durThe implant this encoder is bound to, or None (read-only)
interphase_durn_levelsphase_durpulsestretch- 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 whatImageStimulusandVideoStimulusproduce. 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:
- Raises:
DimensionMismatchError – If
sourceis not dimensionless.
- property implant
The implant this encoder is bound to, or None (read-only)