pulse2percept.stimuli

Visual and electrical stimuli.

Core stimulus types are available at the top level. Generated psychophysics stimuli and bundled sample assets live in the psychophysics and samples namespaces, respectively.

Core

Stimulus()

A stimulus is comprised of a labeled 2D NumPy array that contains the data, where the rows denote electrodes and the columns denote points in time.

ImageStimulus()

A stimulus made from an image, where each pixel gets assigned to an electrode, and grayscale values in the range [0, 255] get converted to activation values in the range [0, 1].

VideoStimulus()

A stimulus made from a movie file, where each pixel gets assigned to an electrode, and grayscale values in the range [0, 255] get assigned to activation values in the range [0, 1].

Electrical stimuli

MonophasicPulse(amp, phase_dur[, delay_dur, ...])

Monophasic pulse

BiphasicPulse(amp, phase_dur[, ...])

Symmetric biphasic pulse

AsymmetricBiphasicPulse(amp1, amp2, ...[, ...])

Asymmetric biphasic pulse

PulseTrain(freq, pulse[, n_pulses, ...])

Generic pulse train

BiphasicPulseTrain(freq, amp, phase_dur[, ...])

Symmetric biphasic pulse train

BiphasicTripletTrain(freq, amp, phase_dur[, ...])

Biphasic pulse triplets

AsymmetricBiphasicPulseTrain(freq, amp1, ...)

Asymmetric biphasic pulse

Encoders

Convert visual stimuli to electrical stimulation.

Encoder([implant])

Base class for encoders.

ImplantEncoder([implant])

Base class for encoders installable on an implant.

PulseEncoder([implant, phase_dur, ...])

Abstract base class for electrical pulse-train encoders.

AmplitudeEncoder([implant, amp_range, freq])

Encode gray levels as pulse amplitudes

FrequencyEncoder([implant, freq_range, amp])

Encode gray levels as pulse train frequencies

TraceEncoder(model, *[, amp, freq, ...])

Encode a traced shape as sequential single-electrode pulses

PhotovoltaicEncoder([implant, grayscale, ...])

Encode image/video gray levels as pulsed optical stimulation

PRIMAEncoder([implant, irradiance, freq, ...])

Encode image/video gray levels for the PRIMA projector.

Psychophysics

Generated visual stimuli in degrees of visual angle and physical time.

psychophysics.bar([width, direction, speed, ...])

Moving bar

psychophysics.grating([spatial_freq, ...])

Sinusoidal grating

psychophysics.landolt_c([gap, position, ...])

Landolt C optotype

psychophysics.tumbling_e([stroke, position, ...])

Tumbling E optotype

Samples

Bundled images and videos for examples, documentation, and tests.

samples.big_buck_bunny([resize, electrodes, ...])

Big Buck Bunny video clip

samples.bvl_cake([resize, electrodes, ...])

Bionic Vision Lab cake photograph

samples.cajal_retina([resize, electrodes, ...])

Cajal's drawing of the retina

samples.logo_bvl([resize, electrodes, ...])

Bionic Vision Lab (BVL) logo

samples.logo_ucsb([resize, electrodes, metadata])

UCSB logo

samples.ucsb_bike([resize, electrodes, ...])

UCSB bike path photograph

samples.ucsb_flyover([resize, electrodes, ...])

UCSB aerial flyover clip

samples.ucsb_pedestrians([resize, ...])

UCSB pedestrians clip

samples.ucsb_surf([resize, electrodes, ...])

UCSB coastline video frame

samples.zebrafish_retina([resize, ...])

Zebrafish retina micrograph

Deprecated in v0.11

These compatibility classes will be removed in v0.12.

BarStimulus(**kwargs)

Drifting bar

GratingStimulus(**kwargs)

Drifting sinusoidal grating

LogoBVL(**kwargs)

Bionic Vision Lab (BVL) logo

LogoUCSB(**kwargs)

UCSB logo

class pulse2percept.stimuli.AmplitudeEncoder(implant=None, amp_range=(0, 50), freq=20, **kwargs)[source]

Encode gray levels as pulse amplitudes

Every electrode emits a pulse train of the same fixed frequency, and the gray level of the pixel it sees sets the amplitude of those pulses. This is how most retinal prostheses encode a video.

Because every electrode shares one pulse period, a raster costs no frequency here: the groups hold fixed offsets from one another and so can never drift together, which means nothing has to be quantized and freq is delivered exactly. With no explicit group_dur the groups also divide that period evenly, one turn each per pulse; an explicit group_dur packs them into a shorter sweep at the start of every period instead. Either way no two groups are ever active at the same instant.

Added in version 0.10.0.

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

  • amp_range ((min_amp, max_amp), optional) –

    Range of pulse amplitudes, in uA or in multiples of perceptual threshold (xTh). A gray level of 0 maps onto min_amp and a gray level of 1 onto max_amp.

    Bare numbers mean uA. Both endpoints must carry the same dimension, so a threshold-relative range is spelled (0 * xTh, 3 * xTh); the half spelling (0, 3 * xTh) is rejected rather than guessed at. A threshold-relative range encodes to a xTh stimulus, which an implant converts to current when it has thresholds for every driven electrode.

    Changed in version 0.11.0: Accepts xTh as well as current.

  • freq (float, optional) –

    Pulse train frequency (Hz), the same for every electrode. The pulse clock runs independently of the video, so the frame rate has no say in the rate delivered. Because every electrode shares this one period, a raster does not quantize it either: the groups keep a fixed offset from one another and cannot drift together. Only clock can lower it, by rounding the period up to a whole number of cycles.

    Note

    A frequency below the frame rate is realizable, but wasteful: some frames then receive no pulse at all and their gray levels are never delivered. Encoding warns when this happens.

  • phase_dur – See PulseEncoder.

  • interphase_dur – See PulseEncoder.

  • cathodic_first – 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, mapping gray levels onto 0-50 uA at 20 Hz:

>>> import numpy as np
>>> import pulse2percept as p2p
>>> video = p2p.stimuli.VideoStimulus(np.random.rand(16, 20, 30),
...                                   metadata={'fps': 20})
>>> implant = p2p.implants.retina.ArgusII()
>>> encoder = p2p.stimuli.AmplitudeEncoder(implant, amp_range=(0, 50))
>>> stim = encoder.encode(video)

The same thing, with the implant encoding its own input:

>>> implant.encoder = p2p.stimuli.AmplitudeEncoder(amp_range=(0, 50))
>>> stim = implant.prepare_stim(video)
amp_unit

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)

class pulse2percept.stimuli.AsymmetricBiphasicPulse(amp1, amp2, phase_dur1, phase_dur2, interphase_dur=0, delay_dur=0, stim_dur=None, cathodic_first=True, electrode=None)[source]

Asymmetric biphasic pulse

A simple stimulus consisting of a single biphasic pulse: a cathodic and an anodic phase, optionally separated by an interphase gap. The two pulse phases can have different amplitudes and duration (“asymmetric”).

Added in version 0.6.

Changed in version 0.10.0: The pulse retains the parameters that define it, and generates its sampled waveform only when one is asked for.

Parameters:
  • amp1 (float) – Current amplitude (uA) of the first and second pulse phases. Negative currents: cathodic, positive: anodic. The signs will be converted automatically depending on cathodic_first.

  • amp2 (float) – Current amplitude (uA) of the first and second pulse phases. Negative currents: cathodic, positive: anodic. The signs will be converted automatically depending on cathodic_first.

  • phase_dur1 (float) – Duration (ms) of the first and second pulse phases.

  • phase_dur2 (float) – Duration (ms) of the first and second pulse phases.

  • interphase_dur (float, optional, default: 0) – Duration (ms) of the gap between cathodic and anodic phases.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the first pulse phase after delay_dur ms.

  • stim_dur (float, optional, default:) – 2*phase_dur+interphase_dur+delay_dur Total stimulus duration (ms). Zeros will be inserted at the end of the stimulus to make the stimulus last stim_dur ms overall.

  • cathodic_first (bool, optional, default: True) – If True, will deliver the cathodic pulse phase before the anodic one.

  • electrode ({ int | string }, optional, default: 0) – Optionally, you can provide your own electrode name.

Notes

  • The order of the two phases is given by the cathodic_first flag.

  • The sign of amp will be automatically adjusted depending on the cathodic_first flag.

  • A pulse will be considered “charge-balanced” if its net current is smaller than 10 picoamps.

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

  • The parameters above are read-only. A pulse with different parameters is a different pulse; build another one.

  • amp1 and amp2 read back as magnitudes, because that is all of them that reaches the waveform: the constructor takes np.abs of each and gets the polarity from cathodic_first.

Examples

An asymmetric cathodic-first pulse (first phase: -40uA, 1ms; second phase: 10uA, 4ms; 1ms interphase-gap) delivered after 2ms and embedded in a stimulus that lasts 15ms overall:

>>> from pulse2percept.stimuli import AsymmetricBiphasicPulse
>>> pulse = AsymmetricBiphasicPulse(-40, 10, 1, 4, interphase_dur=1,
...                                 delay_dur=2, stim_dur=15)
property amp1

Magnitude (uA) of the first pulse phase

property amp2

Magnitude (uA) of the second pulse phase

property phase_dur1

Duration (ms) of the first pulse phase

property phase_dur2

Duration (ms) of the second pulse phase

property interphase_dur

Duration (ms) of the gap between the two phases

property delay_dur

Delay (ms) before the first phase is delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

The waveform is built to end exactly at stim_dur, so this is known without generating one.

property cathodic_first

Whether the cathodic phase is delivered first

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.AsymmetricBiphasicPulseTrain(freq, amp1, amp2, phase_dur1, phase_dur2, interphase_dur=0, delay_dur=0, n_pulses=None, stim_dur=1000.0, cathodic_first=True, electrode=None, metadata=None)[source]

Asymmetric biphasic pulse

A simple stimulus consisting of a single biphasic pulse: a cathodic and an anodic phase, optionally separated by an interphase gap. The two pulse phases can have different amplitudes and duration (“asymmetric”). The order of the two phases is given by the cathodic_first flag.

Added in version 0.6.

Parameters:
  • freq (float) – Pulse train frequency (Hz).

  • amp1 (float) – Current amplitude (uA) of the first and second pulse phases. Negative currents: cathodic, positive: anodic. The signs will be converted automatically depending on cathodic_first.

  • amp2 (float) – Current amplitude (uA) of the first and second pulse phases. Negative currents: cathodic, positive: anodic. The signs will be converted automatically depending on cathodic_first.

  • phase_dur1 (float) – Duration (ms) of the first and second pulse phases.

  • phase_dur2 (float) – Duration (ms) of the first and second pulse phases.

  • interphase_dur (float, optional, default: 0) – Duration (ms) of the gap between cathodic and anodic phases.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the first pulse phase after delay_dur ms.

  • n_pulses (int) – Number of pulses requested in the pulse train. If None, the entire stimulation window (stim_dur) is filled.

  • stim_dur (float, optional, default: 1000 ms) – Total stimulus duration (ms). Zeros will be inserted at the end of the stimulus to make the the stimulus last stim_dur ms overall.

  • cathodic_first (bool, optional, default: True) – If True, will deliver the cathodic pulse phase before the anodic one.

  • electrode ({ int | string }, optional, default: 0) – Optionally, you can provide your own electrode name.

  • metadata (dict) – A dictionary of meta-data

Notes

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

property freq

Pulse train frequency (Hz)

property n_pulses

Number of pulses delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

property amp1

Magnitude (uA) of the first phase of each pulse

property amp2

Magnitude (uA) of the second phase of each pulse

property phase_dur1

Duration (ms) of the first pulse phase

property phase_dur2

Duration (ms) of the second pulse phase

property interphase_dur

Duration (ms) of the gap between the two phases

property delay_dur

Delay (ms) before the first phase of each pulse

property cathodic_first

Whether the cathodic phase is delivered first

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.BarStimulus(**kwargs)[source]

Drifting bar

A drifting bar stimulus.

Added in version 0.7.

Parameters:
  • shape ((height, width)) – A tuple specifying the desired height (pixels) and the width (pixels) of the grating stimulus.

  • direction (scalar in [0, 360) degrees or Quantity, optional) – Drift direction of the bar.

  • speed (scalar in pixels/frame, optional) – Drift speed of the bar.

  • bar_width (scalar in pixels, optional) – The width of the center of the bar.

  • edge_width (scalar in pixels, optional) – The width of the cosine edges of the bar. An edge of width edge_width will be tacked onto both sides of the bar, so the total width will be bar_width + 2 * edge_width

  • px_btw_bars (scalar in pixels, optional) – The number of pixels between the bars in the stimulus.

  • start_pos (scalar in pixels, optional) – The starting position of the first bar. The coordinate system is a line lying along the direction of the bar motion passing through the center of the stimulus. The point 0 is the center of the stimulus.

  • contrast (scalar in [0, 1], optional) – Stimulus contrast between 0 and 1

  • time (scalar, array-like, or None; optional) –

    The time points at which to evaluate the drifting bar:

    • If a scalar, time is interpreted as the end time (in milliseconds) of a time series with 50 Hz frame rate.

    • If array-like, time is interpreted as the exact time points (in milliseconds) at which to draw the bar (end point included).

    • If None, time defaults to a 1-second time series at 50 Hz frame rate (end point included).

  • mask ({'gauss', 'circle', None}) –

    Stimulus mask:

    • ”gauss”: a 2D Gaussian designed such that the border of the image lies at 3 standard deviations

    • ”circle”: a circle that fits into the shape of the stimulus

    • None: no mask

  • electrodes (int, string or list thereof; optional, default: None) – Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

  • metadata (dict, optional, default: None) – Additional stimulus metadata can be stored in a dictionary.

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to each frame of the video

Changed in version 0.10.0: func may now change the shape of a frame, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to each frame in the video. Must accept a 2D or 3D image and return a 2D or 3D image. The returned frames need not have the same shape as the originals (but must all have the same shape as each other); see electrodes.

  • *args – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of a frame alone, and the result is named after its place in the new frame otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in a returned frame.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new video

Return type:

VideoStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

compress()[source]

Compress the source data

Also brings vid_shape back in line with the compressed data: compression drops the time points at which the video does not change, so the frame count of the source is no longer the frame count of the stimulus. Every data.reshape(vid_shape) in this module relies on that invariant. (Compression can also drop all-zero pixels, in which case no shape describes the data any more; see _frames.)

Returns:

compressed

Return type:

VideoStimulus

crop(idx_space=None, idx_time=None, left=0, right=0, top=0, bottom=0, front=0, back=0, electrodes=None)[source]

Crop the video

This method maps a rectangle (defined by two corners) from each video frame to a rectangle of the given size. Similarly, the video can be shortened to a specified range of frames.

Alternatively, this method can be used to crop a number of columns either from the left or the right of the video frame, or a number of rows either from the top or the bottom, or a number of frames from the front (beginning) or back (end) of the video.

Added in version 0.8.

Parameters:
  • idx_space (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • idx_time (tuple (t0, t1)) – Frame indices defining the start t0 and end t1 of the cropped video.

  • left (int) – Number of columns to crop from the left of each video frame

  • right (int) – Number of columns to crop from the right of each video frame

  • top (int) – Number of rows to crop from the top of each video frame

  • bottom (int) – Number of rows to crop from the bottom of each video frame

  • front (int) – Number of frames to crop from the front (beginning) of the video

  • back (int) – Number of frames to crop from the back (end) of the video

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the video

Return type:

VideoStimulus

crop_square()[source]

Crop each video frame to the largest centered square

Frames are cropped without resizing. If their dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

VideoStimulus

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the video using amplitude modulation

Encodes every frame of the video as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses. Each train lasts one frame period.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), each frame receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz).

  • implant (Implant, optional) – If given, the video is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution. Strongly recommended: a video has orders of magnitude more pixels than an implant has electrodes.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

filter(filt, **kwargs)[source]

Filter each frame of the video

Parameters:
  • filt (str) –

    Image filter that will be applied to every frame of the video. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scarr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

VideoStimulus

invert()[source]

Invert the gray levels of the video

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

VideoStimulus

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

play(fps=None, repeat=True, annotate_time=True, ax=None, fmt='jpg')[source]

Animate the video as HTML with JavaScript

The video will be played in an interactive player in IPython or Jupyter Notebook.

Parameters:
  • fps (float or None) – If None, uses the video’s time axis. Not supported for non-homogeneous time axis. May be given as a plain number of hertz or as a unitful frequency (e.g. 30 * Hz, 0.03 * kHz); see pulse2percept.units.

  • repeat (bool, optional) – Whether the animation should repeat when the sequence of frames is completed.

  • annotate_time (bool, optional) – If True, the time of the frame will be shown as t = X ms in the title of the panel.

  • ax (matplotlib.axes.AxesSubplot, optional) – A Matplotlib axes object. If None, will create a new Axes object

  • fmt ({'jpg', 'png'}, optional) –

    The image format used to embed the frames. ‘jpg’ keeps notebooks and doc pages an order of magnitude smaller; use ‘png’ if you need the frames to be pixel-exact.

    Added in version 0.10.0.

Returns:

ani – A Matplotlib animation object that will play the video frame-by-frame.

Return type:

pulse2percept.utils.HTMLAnimation

Notes

Changed in version 0.10.0: The HTML player is now generated by HTMLAnimation, which renders the figure once and ships all frames as a single sprite sheet. This is roughly two orders of magnitude faster than Matplotlib’s to_jshtml and produces much smaller notebooks and doc pages.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

resize(shape, electrodes=None, **kwargs)[source]

Resize the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of each frame in the resized video. If one of the dimensions is set to -1, its value will be inferred by keeping a constant aspect ratio.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the resized video.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary video binary).

Returns:

stim – A copy of the stimulus object containing the resized video

Return type:

VideoStimulus

rgb2gray(electrodes=None)[source]

Convert the video to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale video.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

VideoStimulus

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate each frame of the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate each video frame (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the frame, in which case the enlarged video is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow each frame so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated video

Return type:

VideoStimulus

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

property shape

Data container shape

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

trim(tol=0, electrodes=None)[source]

Remove any black border around the video

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in each frame of the trimmed video.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

VideoStimulus

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.BiphasicPulse(amp, phase_dur, interphase_dur=0, delay_dur=0, stim_dur=None, cathodic_first=True, electrode=None)[source]

Symmetric biphasic pulse

A simple stimulus consisting of a single biphasic pulse: a cathodic and an anodic phase, optionally separated by an interphase gap. Both cathodic and anodic phases have the same duration (“symmetric”).

Added in version 0.6.

Changed in version 0.10.0: The pulse retains the parameters that define it, and generates its sampled waveform only when one is asked for.

Parameters:
  • amp (float) – Current amplitude (uA). Negative currents: cathodic, positive: anodic. The sign will be converted automatically depending on cathodic_first.

  • phase_dur (float) – Duration (ms) of the cathodic/anodic phase.

  • interphase_dur (float, optional, default: 0) – Duration (ms) of the gap between cathodic and anodic phases.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the first pulse phase after delay_dur ms.

  • stim_dur (float, optional, default:) – 2*phase_dur+interphase_dur+delay_dur Total stimulus duration (ms). Zeros will be inserted at the end of the stimulus to make the stimulus last stim_dur ms overall.

  • cathodic_first (bool, optional, default: True) – If True, will deliver the cathodic pulse phase before the anodic one.

  • electrode ({ int | string }, optional, default: 0) – Optionally, you can provide your own electrode name.

Notes

  • The order of the two phases is given by the cathodic_first flag.

  • A biphasic pulse created with this class will always be considered “charge-balanced”.

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

  • The parameters above are read-only. A pulse with different parameters is a different pulse; build another one.

  • amp reads back as a magnitude, because that is all of it that reaches the waveform: the constructor takes np.abs(amp) and gets the polarity from cathodic_first.

Examples

A cathodic-first pulse (1ms phase duration at 20uA, no interphase gap) delivered after 2ms and embedded in a stimulus that lasts 10ms overall:

>>> from pulse2percept.stimuli import BiphasicPulse
>>> pulse = BiphasicPulse(-20, 1, delay_dur=2, stim_dur=10)
property amp

Magnitude (uA) of both pulse phases

property phase_dur

Duration (ms) of the cathodic/anodic phase

property interphase_dur

Duration (ms) of the gap between the two phases

property delay_dur

Delay (ms) before the first phase is delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

The waveform is built to end exactly at stim_dur, so this is known without generating one.

property cathodic_first

Whether the cathodic phase is delivered first

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.BiphasicPulseTrain(freq, amp, phase_dur, interphase_dur=0, delay_dur=0, n_pulses=None, stim_dur=1000.0, cathodic_first=True, electrode=None, metadata=None, threshold_amp=None)[source]

Symmetric biphasic pulse train

A train of symmetric biphasic pulses.

Added in version 0.6.

Parameters:
  • freq (float) – Pulse train frequency (Hz).

  • amp (float) –

    Current amplitude (uA). Negative currents: cathodic, positive: anodic. The sign will be converted automatically depending on cathodic_first.

    May also be given as a multiple of perceptual threshold, e.g. 2 * xTh (see xTh). The current that realizes it is amp * threshold_amp, so without a threshold the train stays measured in xTh.

  • phase_dur (float) – Duration (ms) of the cathodic/anodic phase.

  • interphase_dur (float, optional, default: 0) – Duration (ms) of the gap between cathodic and anodic phases.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the first pulse phase after delay_dur ms.

  • n_pulses (int) – Number of pulses requested in the pulse train. If None, the entire stimulation window (stim_dur) is filled.

  • stim_dur (float, optional, default: 1000 ms) – Total stimulus duration (ms). The pulse train will be trimmed to make the stimulus last stim_dur ms overall.

  • cathodic_first (bool, optional, default: True) – If True, will deliver the cathodic pulse phase before the anodic one.

  • electrode ({ int | string }, optional, default: 0) – Optionally, you can provide your own electrode name.

  • metadata (dict) – A dictionary of meta-data

  • threshold_amp (float, optional) –

    Perceptual threshold (uA) of the electrode this train drives. It is what converts between amp_factor and a current, in either direction.

    Added in version 0.10.0.

Notes

  • Each cycle (“window”) of the pulse train consists of a symmetric biphasic pulse, created with BiphasicPulse.

  • The order and sign of the two phases (cathodic/anodic) of each pulse in the train is automatically adjusted depending on the cathodic_first flag.

  • A pulse train will be considered “charge-balanced” if its net current is smaller than 10 picoamps.

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

  • A train holds a current or a multiple of threshold, and unit says which. Converting between them takes a threshold, so an xTh train without one stays measured in xTh – and is not yet something an implant can deliver.

Examples

Twice threshold, on an electrode whose threshold has been measured:

>>> from pulse2percept.stimuli import BiphasicPulseTrain
>>> from pulse2percept.units import uA, xTh
>>> pt = BiphasicPulseTrain(20, 2 * xTh, 0.45, threshold_amp=80 * uA)
>>> pt.amp, pt.amp_factor
(160.0, 2.0)

The same stimulation, expressed as the current it delivers:

>>> pt = BiphasicPulseTrain(20, 160.0 * uA, 0.45, threshold_amp=80 * uA)
>>> pt.amp, pt.amp_factor
(160.0, 2.0)

With no threshold it is still 2xTh, but it is no longer a current:

>>> pt = BiphasicPulseTrain(20, 2.0 * xTh, 0.45)
>>> pt.amp_factor, pt.unit, pt.threshold_amp
(2.0, xTh, None)
property freq

Pulse train frequency (Hz)

property n_pulses

Number of pulses delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

property amp

Magnitude of both phases of each pulse, in unit

property threshold_amp

Perceptual threshold (uA) this train’s amplitude is relative to

The implant calibration in force, else the threshold this train was built with, else None.

Added in version 0.10.0.

property amp_factor

Amplitude as a multiple of threshold_amp

None only for a current with no threshold to measure it against: an xTh amplitude is the multiple, threshold or no threshold.

Added in version 0.10.0.

property phase_dur

Duration (ms) of the cathodic/anodic phase

property interphase_dur

Duration (ms) of the gap between the two phases

property delay_dur

Delay (ms) before the first phase of each pulse

property cathodic_first

Whether the cathodic phase is delivered first

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.BiphasicTripletTrain(freq, amp, phase_dur, interphase_dur=0, interpulse_dur=0, delay_dur=0, n_pulses=None, stim_dur=1000.0, cathodic_first=True, electrode=None, metadata=None)[source]

Biphasic pulse triplets

A train of symmetric biphasic pulse triplets.

Added in version 0.6.

Parameters:
  • freq (float) – Pulse train frequency (Hz).

  • amp (float) – Current amplitude (uA). Negative currents: cathodic, positive: anodic. The sign will be converted automatically depending on cathodic_first.

  • phase_dur (float) – Duration (ms) of the cathodic/anodic phase.

  • interphase_dur (float, optional, default: 0) – Duration (ms) of the gap between cathodic and anodic phases.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the first pulse phase after delay_dur ms.

  • interpulse_dur (float, optional, default: 0) – Delay duration (ms) between each biphasic pulse within the train. Note, this delay is also applied after the third biphasic pulse

  • n_pulses (int) – Number of pulses requested in the pulse train. If None, the entire stimulation window (stim_dur) is filled.

  • stim_dur (float, optional, default: 1000 ms) – Total stimulus duration (ms). The pulse train will be trimmed to make the stimulus last stim_dur ms overall.

  • cathodic_first (bool, optional, default: True) – If True, will deliver the cathodic pulse phase before the anodic one.

  • electrode ({ int | string }, optional, default: 0) – Optionally, you can provide your own electrode name.

  • metadata (dict) – A dictionary of meta-data

Notes

  • Each cycle (“window”) of the pulse train consists of three biphasic pulses, created with BiphasicPulse.

  • The order and sign of the two phases (cathodic/anodic) of each pulse in the train is automatically adjusted depending on the cathodic_first flag.

  • A pulse train will be considered “charge-balanced” if its net current is smaller than 10 picoamps.

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

property freq

Pulse train frequency (Hz)

property n_pulses

Number of pulses delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

property amp

Magnitude (uA) of both phases of each pulse

property phase_dur

Duration (ms) of the cathodic/anodic phase

property interphase_dur

Duration (ms) of the gap between the two phases

property interpulse_dur

Delay (ms) after each pulse of the triplet

property delay_dur

Delay (ms) before the first phase of each pulse

property cathodic_first

Whether the cathodic phase is delivered first

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.Encoder(implant=None)[source]

Base class for encoders.

An encoder converts a desired visual target into stimulation for a prosthetic system. Each subclass defines the target representation it accepts and implements encode().

Only an ImplantEncoder can be installed on Implant.encoder.

Added in version 0.11.0.

Parameters:

implant (Implant, optional) – The implant to encode for. Once bound, an encoder cannot be rebound.

property implant

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

abstractmethod encode(source)[source]

Encode a visual target as stimulation

Parameters:

source – The visual target to encode, in the representation the subclass accepts.

Returns:

stim – The encoded stimulus, in the unit that drives the device.

Return type:

Stimulus

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)
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)

class pulse2percept.stimuli.GratingStimulus(**kwargs)[source]

Drifting sinusoidal grating

A drifting sinusoidal grating of a given spatial and temporal frequency.

Added in version 0.7.

Parameters:
  • shape ((height, width)) – A tuple specifying the desired height (pixels) and the width (pixels) of the grating stimulus.

  • direction (scalar in [0, 360) degrees or Quantity, optional) – Drift direction of the grating.

  • spatial_freq (scalar (cycles/pixel), optional) – Spatial frequency of the grating in cycles per pixel

  • temporal_freq (scalar (cycles/frame), optional) – Temporal frequency of the grating in cycles per frame

  • phase (scalar (degrees) or Quantity, optional) – The initial phase of the grating in degrees

  • contrast (scalar in [0, 1], optional) – Stimulus contrast between 0 and 1

  • time (scalar, array-like, or None; optional) –

    The time points at which to evaluate the drifting grating:

    • If a scalar, time is interpreted as the end time (in milliseconds) of a time series with 50 Hz frame rate.

    • If array-like, time is interpreted as the exact time points (in milliseconds) at which to draw the grating (end point included).

    • If None, time defaults to a 1-second time series at 50 Hz frame rate (end point included).

  • mask ({'gauss', 'circle', None}) –

    Stimulus mask:

    • ”gauss”: a 2D Gaussian designed such that the border of the image lies at 3 standard deviations

    • ”circle”: a circle that fits into the shape of the stimulus

    • None: no mask

  • electrodes (int, string or list thereof; optional, default: None) – Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

  • metadata (dict, optional, default: None) – Additional stimulus metadata can be stored in a dictionary.

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to each frame of the video

Changed in version 0.10.0: func may now change the shape of a frame, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to each frame in the video. Must accept a 2D or 3D image and return a 2D or 3D image. The returned frames need not have the same shape as the originals (but must all have the same shape as each other); see electrodes.

  • *args – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of a frame alone, and the result is named after its place in the new frame otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in a returned frame.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new video

Return type:

VideoStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

compress()[source]

Compress the source data

Also brings vid_shape back in line with the compressed data: compression drops the time points at which the video does not change, so the frame count of the source is no longer the frame count of the stimulus. Every data.reshape(vid_shape) in this module relies on that invariant. (Compression can also drop all-zero pixels, in which case no shape describes the data any more; see _frames.)

Returns:

compressed

Return type:

VideoStimulus

crop(idx_space=None, idx_time=None, left=0, right=0, top=0, bottom=0, front=0, back=0, electrodes=None)[source]

Crop the video

This method maps a rectangle (defined by two corners) from each video frame to a rectangle of the given size. Similarly, the video can be shortened to a specified range of frames.

Alternatively, this method can be used to crop a number of columns either from the left or the right of the video frame, or a number of rows either from the top or the bottom, or a number of frames from the front (beginning) or back (end) of the video.

Added in version 0.8.

Parameters:
  • idx_space (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • idx_time (tuple (t0, t1)) – Frame indices defining the start t0 and end t1 of the cropped video.

  • left (int) – Number of columns to crop from the left of each video frame

  • right (int) – Number of columns to crop from the right of each video frame

  • top (int) – Number of rows to crop from the top of each video frame

  • bottom (int) – Number of rows to crop from the bottom of each video frame

  • front (int) – Number of frames to crop from the front (beginning) of the video

  • back (int) – Number of frames to crop from the back (end) of the video

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the video

Return type:

VideoStimulus

crop_square()[source]

Crop each video frame to the largest centered square

Frames are cropped without resizing. If their dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

VideoStimulus

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the video using amplitude modulation

Encodes every frame of the video as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses. Each train lasts one frame period.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), each frame receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz).

  • implant (Implant, optional) – If given, the video is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution. Strongly recommended: a video has orders of magnitude more pixels than an implant has electrodes.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

filter(filt, **kwargs)[source]

Filter each frame of the video

Parameters:
  • filt (str) –

    Image filter that will be applied to every frame of the video. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scarr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

VideoStimulus

invert()[source]

Invert the gray levels of the video

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

VideoStimulus

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

play(fps=None, repeat=True, annotate_time=True, ax=None, fmt='jpg')[source]

Animate the video as HTML with JavaScript

The video will be played in an interactive player in IPython or Jupyter Notebook.

Parameters:
  • fps (float or None) – If None, uses the video’s time axis. Not supported for non-homogeneous time axis. May be given as a plain number of hertz or as a unitful frequency (e.g. 30 * Hz, 0.03 * kHz); see pulse2percept.units.

  • repeat (bool, optional) – Whether the animation should repeat when the sequence of frames is completed.

  • annotate_time (bool, optional) – If True, the time of the frame will be shown as t = X ms in the title of the panel.

  • ax (matplotlib.axes.AxesSubplot, optional) – A Matplotlib axes object. If None, will create a new Axes object

  • fmt ({'jpg', 'png'}, optional) –

    The image format used to embed the frames. ‘jpg’ keeps notebooks and doc pages an order of magnitude smaller; use ‘png’ if you need the frames to be pixel-exact.

    Added in version 0.10.0.

Returns:

ani – A Matplotlib animation object that will play the video frame-by-frame.

Return type:

pulse2percept.utils.HTMLAnimation

Notes

Changed in version 0.10.0: The HTML player is now generated by HTMLAnimation, which renders the figure once and ships all frames as a single sprite sheet. This is roughly two orders of magnitude faster than Matplotlib’s to_jshtml and produces much smaller notebooks and doc pages.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

resize(shape, electrodes=None, **kwargs)[source]

Resize the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of each frame in the resized video. If one of the dimensions is set to -1, its value will be inferred by keeping a constant aspect ratio.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the resized video.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary video binary).

Returns:

stim – A copy of the stimulus object containing the resized video

Return type:

VideoStimulus

rgb2gray(electrodes=None)[source]

Convert the video to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale video.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

VideoStimulus

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate each frame of the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate each video frame (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the frame, in which case the enlarged video is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow each frame so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated video

Return type:

VideoStimulus

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

property shape

Data container shape

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

trim(tol=0, electrodes=None)[source]

Remove any black border around the video

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in each frame of the trimmed video.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

VideoStimulus

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.ImageStimulus[source]

A stimulus made from an image, where each pixel gets assigned to an electrode, and grayscale values in the range [0, 255] get converted to activation values in the range [0, 1].

See also

  • Basic Concepts > Electrical Stimuli <topics-stimuli>

  • VideoStimulus

Added in version 0.7.

Parameters:
  • source (str, os.PathLike, ImageStimulus, or np.ndarray) –

    Path to an image file (str or pathlib.Path). File types are inferred from the file ending (support types include JPG, PNG, and TIF).

    Note

    For GIFs, use VideoStimulus.

    Changed in version 0.11.0: A pathlib.Path is accepted wherever a filename is. metadata['source'] is always a string.

  • resize ((height, width) or None, optional) – Shape of the resized image. If one of the dimensions is set to -1, its value will be inferred by keeping a constant aspect ratio.

  • as_gray (bool, optional) – Flag whether to convert the image to grayscale. A four-channel image is interpreted as RGBA (e.g., a PNG), and the alpha channel will be blended with the color black.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the (resized) image.

  • metadata (dict, optional) – Additional stimulus metadata can be stored in a dictionary.

  • compress (bool, optional) – If True, will remove pixels with 0 grayscale value.

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to the image

Changed in version 0.10.0: func may now change the shape of the image, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to the image. Must accept a 2D or 3D image and return a 2D or 3D image. The returned image need not have the same shape as the original; see electrodes.

  • args (*) – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of the image alone, and the result is named after its place in the new image otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in the returned image.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new image

Return type:

ImageStimulus

invert()[source]

Invert the gray levels of the image

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

ImageStimulus

rgb2gray(electrodes=None)[source]

Convert the image to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale image.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

ImageStimulus

Notes

  • A four-channel image is interpreted as RGBA (e.g., a PNG), and the alpha channel will be blended with the color black.

resize(shape, electrodes=None, **kwargs)[source]

Resize the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of the resized image

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the grayscale image.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary image binary).

Returns:

stim – A copy of the stimulus object containing the resized image

Return type:

ImageStimulus

crop(idx_rect=None, left=0, right=0, top=0, bottom=0, electrodes=None)[source]

Crop the image

This method maps a rectangle (defined by two corners) from the image to a rectangle of the given size. Alternatively, this method can be used to crop a number of columns either from the left or the right of the image, or a number of rows either from the top or the bottom.

Added in version 0.8.

Parameters:
  • idx_rect (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • left (int) – Number of columns to crop from the left

  • right (int) – Number of columns to crop from the right

  • top (int) – Number of rows to crop from the top

  • bottom (int) – Number of rows to crop from the bottom

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the cropped image

Return type:

ImageStimulus

crop_square()[source]

Crop the image to the largest centered square

The image is cropped without resizing. If its dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

ImageStimulus

trim(tol=0, electrodes=None)[source]

Remove any black border around the image

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the trimmed image.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

ImageStimulus

threshold(thresh, **kwargs)[source]

Threshold the image

Parameters:

thresh (str or float) –

If a float in [0,1] is provided, pixels whose grayscale value is above said threshold will be white, others black.

A number of additional methods are supported:

  • ’mean’: Threshold image based on the mean of grayscale values.

  • ’minimum’: Threshold image based on the minimum method, where

    the histogram of the input image is computed and smoothed until there are only two maxima.

  • ’local’: Threshold image based on local pixel neighborhood. Requires block_size: odd number of pixels in the neighborhood.

  • ’otsu’: Otsu’s method

  • ’isodata’: ISODATA method, also known as the Ridler-Calvard method or intermeans.

Returns:

stim – A copy of the stimulus object with two gray levels 0.0 and 1.0

Return type:

ImageStimulus

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate the image (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the canvas, in which case the enlarged image is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow the image so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated image

Return type:

ImageStimulus

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

filter(filt, **kwargs)[source]

Filter the image

Parameters:
  • filt (str) –

    Image filter. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scharr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

ImageStimulus

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the image using amplitude modulation

Encodes the image as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), the image receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz). The image is treated as a single frame lasting 500 ms unless frame_dur says otherwise.

  • implant (Implant, optional) – If given, the image is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

plot(ax=None, **kwargs)[source]

Plot the stimulus

Parameters:

ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object or a list thereof (one per electrode to plot). If None, a new Axes object will be created.

Returns:

ax – Returns the axes with the plot on it

Return type:

matplotlib.axes.Axes

save(fname, vmin=0, vmax=None)[source]

Save the stimulus as an image

Parameters:

fname (str or os.PathLike) –

The name of the image file to be created. Image type will be inferred from the file extension.

Changed in version 0.11.0: A pathlib.Path is accepted.

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.ImplantEncoder(implant=None)[source]

Base class for encoders installable on an implant.

An implant encoder accepts the dimensionless Stimulus (image or video gray levels) that Implant.prepare_stim produces, and can therefore be assigned to Implant.encoder. Assigning an unbound encoder binds it to that implant.

Added in version 0.11.0.

abstractmethod encode(source)[source]

Encode a visual target as stimulation

Parameters:

source – The visual target to encode, in the representation the subclass accepts.

Returns:

stim – The encoded stimulus, in the unit that drives the device.

Return type:

Stimulus

property implant

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

class pulse2percept.stimuli.LogoBVL(**kwargs)[source]

Bionic Vision Lab (BVL) logo

Load the 576x720x4 Bionic Vision Lab (BVL) logo.

Added in version 0.7.

Parameters:
  • resize ((height, width) or None, optional) – A tuple specifying the desired height and the width of the image stimulus.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the (resized) image.

  • metadata (dict, optional) – Additional stimulus metadata can be stored in a dictionary.

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to the image

Changed in version 0.10.0: func may now change the shape of the image, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to the image. Must accept a 2D or 3D image and return a 2D or 3D image. The returned image need not have the same shape as the original; see electrodes.

  • args (*) – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of the image alone, and the result is named after its place in the new image otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in the returned image.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new image

Return type:

ImageStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

compress()[source]

Compress the source data in place

crop(idx_rect=None, left=0, right=0, top=0, bottom=0, electrodes=None)[source]

Crop the image

This method maps a rectangle (defined by two corners) from the image to a rectangle of the given size. Alternatively, this method can be used to crop a number of columns either from the left or the right of the image, or a number of rows either from the top or the bottom.

Added in version 0.8.

Parameters:
  • idx_rect (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • left (int) – Number of columns to crop from the left

  • right (int) – Number of columns to crop from the right

  • top (int) – Number of rows to crop from the top

  • bottom (int) – Number of rows to crop from the bottom

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the cropped image

Return type:

ImageStimulus

crop_square()[source]

Crop the image to the largest centered square

The image is cropped without resizing. If its dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

ImageStimulus

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the image using amplitude modulation

Encodes the image as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), the image receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz). The image is treated as a single frame lasting 500 ms unless frame_dur says otherwise.

  • implant (Implant, optional) – If given, the image is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

filter(filt, **kwargs)[source]

Filter the image

Parameters:
  • filt (str) –

    Image filter. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scharr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

ImageStimulus

invert()[source]

Invert the gray levels of the image

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

ImageStimulus

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(ax=None, **kwargs)[source]

Plot the stimulus

Parameters:

ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object or a list thereof (one per electrode to plot). If None, a new Axes object will be created.

Returns:

ax – Returns the axes with the plot on it

Return type:

matplotlib.axes.Axes

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

resize(shape, electrodes=None, **kwargs)[source]

Resize the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of the resized image

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the grayscale image.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary image binary).

Returns:

stim – A copy of the stimulus object containing the resized image

Return type:

ImageStimulus

rgb2gray(electrodes=None)[source]

Convert the image to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale image.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

ImageStimulus

Notes

  • A four-channel image is interpreted as RGBA (e.g., a PNG), and the alpha channel will be blended with the color black.

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate the image (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the canvas, in which case the enlarged image is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow the image so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated image

Return type:

ImageStimulus

save(fname, vmin=0, vmax=None)[source]

Save the stimulus as an image

Parameters:

fname (str or os.PathLike) –

The name of the image file to be created. Image type will be inferred from the file extension.

Changed in version 0.11.0: A pathlib.Path is accepted.

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

property shape

Data container shape

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

threshold(thresh, **kwargs)[source]

Threshold the image

Parameters:

thresh (str or float) –

If a float in [0,1] is provided, pixels whose grayscale value is above said threshold will be white, others black.

A number of additional methods are supported:

  • ’mean’: Threshold image based on the mean of grayscale values.

  • ’minimum’: Threshold image based on the minimum method, where

    the histogram of the input image is computed and smoothed until there are only two maxima.

  • ’local’: Threshold image based on local pixel neighborhood. Requires block_size: odd number of pixels in the neighborhood.

  • ’otsu’: Otsu’s method

  • ’isodata’: ISODATA method, also known as the Ridler-Calvard method or intermeans.

Returns:

stim – A copy of the stimulus object with two gray levels 0.0 and 1.0

Return type:

ImageStimulus

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

trim(tol=0, electrodes=None)[source]

Remove any black border around the image

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the trimmed image.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

ImageStimulus

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.LogoUCSB(**kwargs)[source]

UCSB logo

Load a 324x727 white-on-black logo of the University of California, Santa Barbara.

Added in version 0.7.

Parameters:
  • resize ((height, width) or None, optional) – A tuple specifying the desired height and the width of the image stimulus.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the (resized) image.

  • metadata (dict, optional) – Additional stimulus metadata can be stored in a dictionary.

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to the image

Changed in version 0.10.0: func may now change the shape of the image, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to the image. Must accept a 2D or 3D image and return a 2D or 3D image. The returned image need not have the same shape as the original; see electrodes.

  • args (*) – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of the image alone, and the result is named after its place in the new image otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in the returned image.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new image

Return type:

ImageStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

compress()[source]

Compress the source data in place

crop(idx_rect=None, left=0, right=0, top=0, bottom=0, electrodes=None)[source]

Crop the image

This method maps a rectangle (defined by two corners) from the image to a rectangle of the given size. Alternatively, this method can be used to crop a number of columns either from the left or the right of the image, or a number of rows either from the top or the bottom.

Added in version 0.8.

Parameters:
  • idx_rect (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • left (int) – Number of columns to crop from the left

  • right (int) – Number of columns to crop from the right

  • top (int) – Number of rows to crop from the top

  • bottom (int) – Number of rows to crop from the bottom

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the cropped image

Return type:

ImageStimulus

crop_square()[source]

Crop the image to the largest centered square

The image is cropped without resizing. If its dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

ImageStimulus

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the image using amplitude modulation

Encodes the image as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), the image receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz). The image is treated as a single frame lasting 500 ms unless frame_dur says otherwise.

  • implant (Implant, optional) – If given, the image is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

filter(filt, **kwargs)[source]

Filter the image

Parameters:
  • filt (str) –

    Image filter. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scharr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

ImageStimulus

invert()[source]

Invert the gray levels of the image

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

ImageStimulus

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(ax=None, **kwargs)[source]

Plot the stimulus

Parameters:

ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object or a list thereof (one per electrode to plot). If None, a new Axes object will be created.

Returns:

ax – Returns the axes with the plot on it

Return type:

matplotlib.axes.Axes

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

resize(shape, electrodes=None, **kwargs)[source]

Resize the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of the resized image

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the grayscale image.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary image binary).

Returns:

stim – A copy of the stimulus object containing the resized image

Return type:

ImageStimulus

rgb2gray(electrodes=None)[source]

Convert the image to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale image.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

ImageStimulus

Notes

  • A four-channel image is interpreted as RGBA (e.g., a PNG), and the alpha channel will be blended with the color black.

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate the image

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate the image (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the canvas, in which case the enlarged image is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow the image so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated image

Return type:

ImageStimulus

save(fname, vmin=0, vmax=None)[source]

Save the stimulus as an image

Parameters:

fname (str or os.PathLike) –

The name of the image file to be created. Image type will be inferred from the file extension.

Changed in version 0.11.0: A pathlib.Path is accepted.

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

property shape

Data container shape

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

threshold(thresh, **kwargs)[source]

Threshold the image

Parameters:

thresh (str or float) –

If a float in [0,1] is provided, pixels whose grayscale value is above said threshold will be white, others black.

A number of additional methods are supported:

  • ’mean’: Threshold image based on the mean of grayscale values.

  • ’minimum’: Threshold image based on the minimum method, where

    the histogram of the input image is computed and smoothed until there are only two maxima.

  • ’local’: Threshold image based on local pixel neighborhood. Requires block_size: odd number of pixels in the neighborhood.

  • ’otsu’: Otsu’s method

  • ’isodata’: ISODATA method, also known as the Ridler-Calvard method or intermeans.

Returns:

stim – A copy of the stimulus object with two gray levels 0.0 and 1.0

Return type:

ImageStimulus

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

trim(tol=0, electrodes=None)[source]

Remove any black border around the image

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the trimmed image.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

ImageStimulus

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.MonophasicPulse(amp, phase_dur, delay_dur=0, stim_dur=None, electrode=None)[source]

Monophasic pulse

A simple stimulus consisting of a single monophasic pulse (either cathodic/negative or anodic/positive).

Added in version 0.6.

Changed in version 0.10.0: The pulse retains the parameters that define it, and generates its sampled waveform only when one is asked for.

Parameters:
  • amp (float) – Current amplitude (uA). Negative currents: cathodic, positive: anodic.

  • phase_dur (float) – Duration (ms) of the cathodic or anodic phase.

  • delay_dur (float) – Delay duration (ms). Zeros will be inserted at the beginning of the stimulus to deliver the pulse after delay_dur ms.

  • stim_dur (float, optional) – Total stimulus duration (ms). Zeros will be inserted at the end of the stimulus to make the stimulus last stim_dur ms overall.

  • electrode ({ int | string }, optional) – Optionally, you can provide your own electrode name.

Notes

  • The sign of amp will determine whether the pulse is cathodic (negative current) or anodic (positive current).

  • A regular monophasic pulse is not considered “charge-balanced”. However, if amp is small enough, the pulse can be considered “charge-balanced” if its net current is smaller than 10 picoamps.

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

  • The parameters above are read-only. A pulse with different parameters is a different pulse; build another one.

Examples

A single cathodic pulse (1ms phase duration at 20uA) delivered after 2ms and embedded in a stimulus that lasts 10ms overall:

>>> from pulse2percept.stimuli import MonophasicPulse
>>> pulse = MonophasicPulse(-20, 1, delay_dur=2, stim_dur=10)
property amp

Current amplitude (uA); negative is cathodic

property phase_dur

Duration (ms) of the cathodic or anodic phase

property delay_dur

Delay (ms) before the pulse is delivered

property stim_dur

Total stimulus duration (ms)

property duration

Stimulus duration (ms)

The waveform is built to end exactly at stim_dur, so this is known without generating one.

property cathodic

Whether the pulse delivers a negative current

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.PhotovoltaicEncoder(implant=None, *, irradiance, freq, pulse_dur, wavelength, grayscale=True, threshold=0.5)[source]

Encode image/video gray levels as pulsed optical stimulation

Photovoltaic subretinal arrays are driven by pulsed near-infrared light. This encoder samples an image or video at the implant’s pixel locations and returns irradiance in mW/mm^2. Peak irradiance is fixed; gray level sets the ON duration within each pulse period.

Added in version 0.11.0.

Changed in version 0.11.0: The implant is passed to the constructor rather than to encode(). The optical parameters are keyword-only.

Parameters:
  • implant (Implant, optional) – The implant to encode for. Its pixel locations are used to sample images and videos and its pixel names label the result. If None, every pixel of the source is its own photovoltaic pixel.

  • irradiance (float or Quantity) – Peak irradiance (mW/mm^2) while a pixel is on.

  • freq (float or Quantity) – Pulse repetition rate (Hz).

  • pulse_dur (float or Quantity) – ON duration (ms) of a fully lit pixel. Must fit into 1000 / freq.

  • wavelength (float or Quantity) – Wavelength (nm) of the illumination. Photovoltaic pixel response is wavelength dependent, so there is no generic default.

  • grayscale (bool, optional) – If True (default), map gray levels to ON duration. If False, use binary off/on encoding.

  • threshold (float, optional) – Binary-mode threshold. The default 0.5 is a pulse2percept convention.

Notes

  • Grayscale mode scales ON duration linearly, gray * pulse_dur. The studies these arrays come from report fixed-duration pulses and do not specify a natural-image grayscale transfer function, so this mapping is a pulse2percept simulation convention, not the experimental encoding protocol. Because durations are continuous, an image with many gray levels produces more distinct time points than a device that quantizes duration (see PRIMAEncoder).

  • Videos are sampled at the pulse rate using zero-order hold.

  • _spatial_view returns normalized time-averaged optical drive, where 1.0 is a fully lit pixel at these settings (ref_drive). It is neither retinal current nor perceptual brightness.

  • Photovoltaic conversion to retinal current is not modeled.

Examples

>>> from pulse2percept.implants.retina import Lorach2015Array
>>> from pulse2percept.stimuli import PhotovoltaicEncoder, samples
>>> encoder = PhotovoltaicEncoder(Lorach2015Array(), irradiance=4,
...                               freq=40, pulse_dur=4, wavelength=915)
>>> encoder.encode(samples.logo_bvl()).unit
mW/mm^2
property period

Pulse period (ms)

property ref_drive

Time-averaged irradiance (mW/mm^2) the normalized view calls 1.0

A fully lit pixel at these settings. Devices with a documented projector maximum normalize against that maximum instead.

encode(source)[source]

Encode an image or a video as near-infrared irradiance

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.

Returns:

stim – The projected irradiance, in mW/mm^2, with a time axis in milliseconds. The waveform is generated only when samples are asked for.

Return type:

Stimulus

Raises:

DimensionMismatchError – If source is not dimensionless.

property implant

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

class pulse2percept.stimuli.PRIMAEncoder(implant=None, irradiance=3.5 mW/mm^2, freq=30 Hz, pulse_dur=9.8 ms, grayscale=True, threshold=0.5)[source]

Encode image/video gray levels for the PRIMA projector.

PRIMA uses 880 nm illumination rather than injected current. The projector uses fixed peak irradiance and pulse-width modulation [Palanker2020], [Holz2026]. This encoder returns irradiance in mW/mm^2.

Unlike the generic PhotovoltaicEncoder, ON durations are quantized onto the projector’s own 0.7 ms duration grid, and normalized drive is referenced to the projector maximum rather than to these settings.

Added in version 0.11.0.

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

  • irradiance (float or Quantity, optional) – Peak irradiance (mW/mm^2) while a pixel is on.

  • freq (float or Quantity, optional) – Projector frame rate (Hz).

  • pulse_dur (float or Quantity, optional) – Maximum ON duration (ms). Must lie on the pulse_step grid and not exceed max_pulse_dur.

  • grayscale (bool, optional) – If True (default), map gray levels to pulse duration. If False, use binary off/on encoding.

  • threshold (float, optional) – Binary-mode threshold. The default 0.5 is a pulse2percept convention.

Notes

  • Pivotal-system defaults are 3.5 mW/mm^2, 30 Hz, and 14 nonzero ON durations from 0.7 to 9.8 ms.

  • Grayscale mode maps normalized intensity linearly to these duration levels. The clinical camera-to-pulse-duration transfer function is not published.

  • Videos are sampled at the projector clock using zero-order hold.

  • _spatial_view returns normalized time-averaged optical drive for spatial models. It is neither retinal current nor perceptual brightness.

  • Clinical image preprocessing is outside this encoder.

Examples

>>> from pulse2percept.implants.retina import PRIMAPivotal
>>> from pulse2percept.stimuli import PRIMAEncoder, samples
>>> PRIMAEncoder(PRIMAPivotal()).encode(samples.logo_bvl()).unit
mW/mm^2
pulse_step = 0.7

Smallest nonzero ON duration (ms) the projector can produce; every other duration is a whole multiple of it

max_pulse_dur = 9.8

Longest documented ON duration (ms), i.e. 14 steps

projector_wavelength = 880.0

Wavelength (nm) the projector illuminates at

max_irradiance = 3.5

Peak irradiance (mW/mm^2) of the pivotal-trial projector

max_freq = 30.0

Frame rate (Hz) of the pivotal-trial projector

max_duty_cycle = 0.294

Largest documented duty cycle, max_freq * max_pulse_dur

ref_drive = 1.029

Time-averaged irradiance (mW/mm^2) the normalized spatial view calls 1.0. Fixed at the projector maximum, so lowering any setting lowers the normalized drive.

property n_levels

Number of nonzero ON durations available up to pulse_dur

encode(source)[source]

Encode an image or a video as near-infrared irradiance

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.

Returns:

stim – The projected irradiance, in mW/mm^2, with a time axis in milliseconds. The waveform is generated only when samples are asked for.

Return type:

Stimulus

Raises:

DimensionMismatchError – If source is not dimensionless.

property implant

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

property period

Pulse period (ms)

class pulse2percept.stimuli.PulseEncoder(implant=None, phase_dur=0.46, interphase_dur=0, cathodic_first=True, pulse=None, clock=None, n_levels=None, frame_dur=None, stretch=False)[source]

Abstract base class for electrical pulse-train encoders.

Maps image or video gray levels onto the parameters of repeated electrical pulses. If the encoder has an implant, the source is sampled at its electrode locations and scheduled using its raster pattern.

Subclasses implement _modulate(), which maps gray levels to pulse amplitude and frequency.

Added in version 0.10.0.

Changed in version 0.11.0: Renamed from StimulusEncoder. The implant is passed to the constructor rather than to encode().

Parameters:
  • implant (Implant, optional) – The implant to encode for. Its electrode locations are used to sample images and videos, its electrode names label the result, and its raster decides which electrodes may pulse when. If None, every pixel of the source is its own electrode and every electrode fires on the same schedule.

  • phase_dur (float, optional) – Duration of each pulse phase (ms).

  • interphase_dur (float, optional) – Gap between cathodic and anodic phases (ms).

  • cathodic_first (bool, optional) – If True, deliver the cathodic phase first.

  • pulse (Stimulus, optional) – Pulse shape to repeat. Its amplitude is normalized away.

  • clock (float, optional) – Stimulator clock period (ms). Pulse periods and raster offsets are rounded to whole clock cycles.

  • n_levels (int, optional) – Number of gray levels used before modulation.

  • frame_dur (float, optional) – Frame duration (ms). If None, infer it from the source.

  • stretch (bool, optional) – If True, stretch source gray levels to [0, 1].

Notes

Plain numbers use the units documented above; unitful values are converted automatically. See pulse2percept.units.

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)

class pulse2percept.stimuli.PulseTrain(freq, pulse, n_pulses=None, stim_dur=1000.0, electrode=None, metadata=None)[source]

Generic pulse train

Can be used to concatenate single pulses into a pulse train.

Added in version 0.6.

Parameters:
  • freq (float) – Pulse train frequency (Hz).

  • pulse (Stimulus) – A Stimulus object containing a single pulse that will be concatenated.

  • n_pulses (int) – Number of pulses requested in the pulse train. If None, the entire stimulation window (stim_dur) is filled.

  • stim_dur (float, optional) – Total stimulus duration (ms). The pulse train will be trimmed to make the stimulus last stim_dur ms overall.

  • electrode ({ int | string }, optional) – Optionally, you can provide your own electrode name.

  • metadata (dict) – A dictionary of meta-data

Notes

  • Only pulses that fit whole are delivered. If the pulse train frequency does not exactly divide stim_dur, the number of pulses is therefore rounded down: a 30 Hz train in a 33.37 ms window has one pulse, not one and a fraction of a second. A partial pulse would leave the train with a net current.

  • A frequency slower than 1000 / stim_dur cannot be realized, since the window still holds one pulse. Pass freq=0 for a silent train.

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

  • The train is measured in whatever pulse was measured in: tiling an electrical pulse gives a train in microamps, and tiling a dimensionless one gives a dimensionless train.

property freq

Pulse train frequency (Hz)

property pulse

The single pulse this train repeats

property n_pulses

Number of pulses delivered

property stim_dur

Total stimulus duration (ms)

property pulse_type

Name of the class the repeated pulse came from

property duration

Stimulus duration (ms)

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

compress()[source]

Compress the source data in place

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
class pulse2percept.stimuli.Stimulus[source]

A stimulus is comprised of a labeled 2D NumPy array that contains the data, where the rows denote electrodes and the columns denote points in time. A stimulus can be created from a variety of source types (e.g., scalars, lists, NumPy arrays, and dictionaries).

The stimulus arrays (data, time, electrodes) and the pulse parameters of a stimulus that has any are read-only. Arbitrary stimuli are kept as sampled waveforms only.

See also

  • Basic Concepts > Electrical Stimuli <topics-stimuli>

Added in version 0.6.

Changed in version 0.10.0: Stimulus arrays and pulse parameters are read-only, and waveforms are generated lazily.

Parameters:
  • source (source type) –

    A valid source type is one of the following:

    • Scalar value: interpreted as the current amplitude delivered to a single electrode (no time component).

    • NumPy array:
      • Nx1 array: interpreted as N current amplitudes delivered to N electrodes (no time component).

      • NxM array: interpreted as N electrodes each receiving M current amplitudes in time.

    In addition, you can also pass a collection of source types. Each element must be a valid source type for a single electrode (e.g., scalar, 1-D array, Stimulus).

    • List or tuple: List elements will be assigned to electrodes in order.

    • Dictionary: Dictionary keys are used to address electrodes by name.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, electrode names will be extracted from the source type (e.g., the keys from a dictionary). If a scalar or NumPy array is passed, electrode names will be numbered 0..N.

    Note

    The number of electrode names provided must match the number of electrodes extracted from the source type (i.e., N).

  • time (int, float or list thereof; optional) –

    Optionally, you can provide the time points of the source data. If none are given, time steps will be numbered 0..M.

    Note

    The number of time points provided must match the number of time points extracted from the source type (i.e., M). Stimuli created from scalars or 1-D NumPy arrays will have no time componenet, in which case you cannot provide your own time points.

  • metadata (dict, optional) – Additional stimulus metadata can be stored in a dictionary.

  • compress (bool, optional) –

    If True, will compress the source data in two ways:

    • Remove electrodes with all-zero activation.

    • Retain only the time points at which the stimulus changes.

    For example, in a pulse train, only the signal edges are saved. This drastically reduces the memory footprint of the stimulus.

Notes

  • Depending on the source type, a stimulus might have a time component or not (e.g., scalars: time=None).

  • You can access the stimulus applied to electrode e at time t by directly indexing into Stimulus[e, t]. In this case, t is not a column index but a time point.

  • If the time point is not explicitly stored in the data container, its value will be automatically interpolated from neighboring values.

  • If a requested time point lies outside the range of stored data, the value of its closest end point will be returned.

  • All transformations return a new stimulus, except for compress() and remove().

Examples

Stimulate a single electrode with -13uA:

>>> from pulse2percept.stimuli import Stimulus
>>> stim = Stimulus(-13)

Stimulate ten electrodes with 0uA:

>>> from pulse2percept.stimuli import Stimulus
>>> stim = Stimulus(np.zeros(10))

Provide new electrode names for an existing Stimulus object:

>>> from pulse2percept.stimuli import Stimulus
>>> old_stim = Stimulus([3, 5])
>>> new_stim = Stimulus(old_stim, electrodes=['new0', 'new1'])

Interpolate the stimulus value at some point in time. Here, the stimulus is a single-electrode ramp stimulus (stimulus value == point in time):

>>> from pulse2percept.stimuli import Stimulus
>>> stim = Stimulus(np.arange(10).reshape((1, -1)))
>>> stim[:, 3.45]
3.45...
compress()[source]

Compress the source data in place

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

shift(dt)[source]

Shift the stimulus in time.

Added in version 0.10.0.

Parameters:

dt (float or Quantity) – Time shift. May be positive or negative. Bare values are interpreted in the stimulus’ time unit.

Returns:

shifted – Shifted copy of the stimulus.

Return type:

Stimulus

Notes

stim >> dt and stim << dt are shorthand for stim.shift(dt) and stim.shift(-dt).

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property shape

Data container shape

property unit

The unit data is expressed in

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)
times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property time

A list of time steps (i.e., the columns in the data container)

property is_compressed

Flag indicating whether the stimulus has been compressed

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property duration

Stimulus duration (ms)

class pulse2percept.stimuli.TraceEncoder(model, *, amp=100 uA, freq=300 Hz, phase_dur=0.17 ms, step_dur=50 ms, interphase_dur=0 ms, cathodic_first=True, clock=None, region=None, threshold=0.5, prune_spurs=False)[source]

Encode a traced shape as sequential single-electrode pulses

A model-aware encoder: it uses the model’s VisualFieldMap and implant placement to stimulate one electrode at a time, in trajectory order, for step_dur each:

image or ordered trajectory in visual field
                  |
        ordered (x, y) trajectory in dva      <- trajectory()
                  |
  model.visual_field_map.from_dva()[region]
                  |
nearest placed physical electrode per sample  <- electrode_sequence()
                  |
  one step_dur pulse train per electrode      <- encode()

Unlike an ImplantEncoder, it cannot be installed on Implant.encoder; call encode() directly.

Accepted targets:

  • An (N, 2) array of ordered (x, y) positions in dva.

  • A grayscale ImageStimulus of a single bright stroke on a dark background (e.g., a letter), placed in the visual field by extent. Pixels above threshold are skeletonized to a one-pixel centerline, which must form one open, nonbranching path. With prune_spurs=True, short endpoint spurs produced by skeletonization near thick corners are removed before topology validation. The path starts at its endpoint that comes first in row-major order (topmost, then leftmost). Skeletonization can bend the end of a thick stroke by a pixel, which can change that endpoint. Use trajectory() to check the direction, or pass an (N, 2) trajectory to set it.

Candidates are activated electrodes whose placed location has a finite to_dva location, i.e. lies in the mapped region. Each sample is mapped on its own, so its electrode does not depend on the other samples.

Consecutive samples that select the same electrode are collapsed into one step, so dense sampling does not lengthen a dwell. A later revisit of the same electrode is a new step.

Inspired by the dynamic letter-tracing paradigm of [Beauchamp2020]. This implementation uses physical electrodes only: it does not implement current steering or virtual electrodes, and does not reproduce that study’s protocol.

Added in version 0.11.0.

Parameters:
  • model (model) – Model that places the implant in tissue. Requires implant, an invertible visual_field_map (from_dva and to_dva), and placed electrode coordinates (implant_position, implant_rotation, implant_depth). The encoder binds to model.implant.

  • amp (float or Quantity, optional) – Pulse amplitude (uA, or xTh for threshold multiples) on the active electrode.

  • freq (float or Quantity, optional) – Pulse train frequency (Hz).

  • phase_dur (float or Quantity, optional) – Duration of each pulse phase (ms).

  • step_dur (float or Quantity, optional) – Stimulation time (ms) per trajectory step.

  • interphase_dur (float or Quantity, optional) – Gap between cathodic and anodic phases (ms).

  • cathodic_first (bool, optional) – If True, deliver the cathodic phase first.

  • clock (float or Quantity, optional) – Stimulator clock period (ms). See PulseEncoder.

  • region (str, optional) – Region of visual_field_map.from_dva() to map onto, e.g. ‘v1’. Required if the map has more than one region.

  • threshold (float, optional) – Gray level in [0, 1] above which an image pixel is part of the trace.

  • prune_spurs (bool, optional) – If True, short endpoint spurs produced by skeletonization near thick corners are removed before topology validation. This can also remove a short real branch. If False, any branch is rejected.

Notes

  • Pulse trains are built by AmplitudeEncoder with frame_dur=step_dur, so the implant’s raster applies.

  • Sparse vertices are not interpolated. To trace a line, pass samples along it. An image trace has one sample per skeleton pixel, so the image resolution sets the sampling density.

  • The electrode sequence is determined by retinotopy and placement, so the path across the physical array can look unlike the visual trajectory.

  • DynaphosModel simulates an encoded stimulus at its own freq and p_dur. Set freq and phase_dur to match them.

  • Models with location_noise are not supported.

Examples

Trace a horizontal line at 2 dva below fixation with Orion in the right hemisphere (left visual field):

>>> import numpy as np
>>> import pulse2percept as p2p
>>> from pulse2percept.units import mm, uA, ms
>>> implant = p2p.implants.cortex.Orion()
>>> model = p2p.models.cortex.DynaphosModel(
...     implant, implant_position=(20, -5) * mm)
>>> encoder = p2p.stimuli.TraceEncoder(model, amp=100 * uA,
...                                    freq=model.freq,
...                                    phase_dur=model.p_dur,
...                                    step_dur=50 * ms)
>>> x = np.linspace(-6, -2, 41)
>>> trace = np.column_stack([x, np.full_like(x, -2)])
>>> encoder.electrode_sequence(trace)
['41', '37', '38']
>>> stim = encoder.encode(trace)

The same line drawn as an image, one pixel per 0.1 dva:

>>> from pulse2percept.units import dva
>>> line = np.zeros((9, 41))
>>> line[4, :] = 1
>>> target = p2p.stimuli.ImageStimulus(line)
>>> extent = (-6.05, -1.95, -2.45, -1.55) * dva
>>> encoder.trajectory(target, extent=extent)[[0, -1]]
array([[-6., -2.],
       [-2., -2.]])
>>> encoder.electrode_sequence(target, extent=extent)
['41', '37', '38']
>>> stim = encoder.encode(target, extent=extent)
trajectory(source, *, extent=None)[source]

Return the ordered visual-field trajectory of a target

Parameters:
  • source ((N, 2) array_like, Quantity, or ImageStimulus) – Ordered (x, y) samples in dva (bare numbers are dva), or a grayscale image of a single stroke.

  • extent ((left, right, bottom, top) Quantity, optional) – Outer edges of the image in dva, as in Scene.extent. Required for an image; not accepted for an (N, 2) trajectory.

Returns:

xy – Ordered (x, y) samples in dva. For an image, one sample per skeleton pixel center.

Return type:

(N, 2) np.ndarray

electrode_sequence(source, *, extent=None)[source]

Return the electrodes a target stimulates, in order

Parameters:
Returns:

names – One electrode name per step_dur step, with consecutive duplicates collapsed.

Return type:

list

encode(source, *, extent=None)[source]

Encode a traced target as sequential pulse trains

Parameters:
Returns:

stim – Pulse trains in uA (or xTh), one step_dur step per selected electrode, lasting n_steps * step_dur ms. Rows are the visited electrodes in electrode-array order.

Return type:

Stimulus

property implant

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

class pulse2percept.stimuli.VideoStimulus[source]

A stimulus made from a movie file, where each pixel gets assigned to an electrode, and grayscale values in the range [0, 255] get assigned to activation values in the range [0, 1].

The frame rate of the movie is used to infer the time points at which to stimulate.

See also

  • Basic Concepts > Electrical Stimuli <topics-stimuli>

  • ImageStimulus

Added in version 0.7.

Parameters:
  • source (str, os.PathLike, VideoStimulus, or np.ndarray) –

    Path to a video file (str or pathlib.Path). File types are inferred from the file ending (support types include MP4, AVI, MOV, and GIF). Enforce a specific format via format.

    Changed in version 0.11.0: A pathlib.Path is accepted wherever a filename is. metadata['source'] is always a string.

    Alternatively, pass a <rows x columns x channels x frames> NumPy array or another VideoStimulus object.

  • format (str) – A video format string supported by imageio, such as ‘MP4’, ‘AVI’, or ‘MOV’. Use if the file type cannot be inferred from source. For a full list of supported formats, see https://imageio.readthedocs.io/en/stable/formats.html.

  • resize ((height, width) or None, optional, default: None) – A tuple specifying the desired height and the width of each video frame

  • as_gray (bool, optional) – Flag whether to convert the image to grayscale. A four-channel image is interpreted as RGBA (e.g., a PNG), and the alpha channel will be blended with the color black.

  • electrodes (int, string or list thereof; optional, default: None) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image: a letter for the row, a number for the column, and a suffix for the color channel (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the (resized) image.

  • metadata (dict, optional, default: None) – Additional stimulus metadata can be stored in a dictionary.

  • compress (bool, optional, default: False) – If True, will compress the source data in two ways: * Remove electrodes with all-zero activation. * Retain only the time points at which the stimulus changes.

  • start_time (float or Quantity, optional, default: None) –

    Load only the frames that start in the half-open interval [start_time, stop_time) of the source video, in milliseconds. Time-based clipping requires the video reader to report a frame rate.

    Note

    The clip starts at time[0] == 0 no matter where it was cut from. To shorten a video that is already in memory, and keep its original time stamps, use crop() instead.

    Added in version 0.10.0.

  • stop_time (float or Quantity, optional, default: None) –

    Load only the frames that start in the half-open interval [start_time, stop_time) of the source video, in milliseconds. Time-based clipping requires the video reader to report a frame rate.

    Note

    The clip starts at time[0] == 0 no matter where it was cut from. To shorten a video that is already in memory, and keep its original time stamps, use crop() instead.

    Added in version 0.10.0.

compress()[source]

Compress the source data

Also brings vid_shape back in line with the compressed data: compression drops the time points at which the video does not change, so the frame count of the source is no longer the frame count of the stimulus. Every data.reshape(vid_shape) in this module relies on that invariant. (Compression can also drop all-zero pixels, in which case no shape describes the data any more; see _frames.)

Returns:

compressed

Return type:

VideoStimulus

apply(func, *args, electrodes=None, **kwargs)[source]

Apply a function to each frame of the video

Changed in version 0.10.0: func may now change the shape of a frame, and electrodes can name the result.

Parameters:
  • func (function) – The function to apply to each frame in the video. Must accept a 2D or 3D image and return a 2D or 3D image. The returned frames need not have the same shape as the originals (but must all have the same shape as each other); see electrodes.

  • *args – Additional positional arguments passed to the function

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, the original names are carried over whenever func leaves the shape of a frame alone, and the result is named after its place in the new frame otherwise (e.g. for skimage.transform.resize).

    Note

    The number of electrode names provided must match the number of pixels in a returned frame.

  • **kwargs – Additional keyword arguments passed to the function

Returns:

stim – A copy of the stimulus object with the new video

Return type:

VideoStimulus

invert()[source]

Invert the gray levels of the video

Returns:

stim – A copy of the stimulus object with all grayscale values inverted in the range [0, 1].

Return type:

VideoStimulus

rgb2gray(electrodes=None)[source]

Convert the video to grayscale

Parameters:

electrodes (int, string or list thereof; optional) –

Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

Note

The number of electrode names provided must match the number of pixels in the grayscale video.

Returns:

stim – A copy of the stimulus object with all RGB values converted to grayscale in the range [0, 1].

Return type:

VideoStimulus

resize(shape, electrodes=None, **kwargs)[source]

Resize the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • shape ((rows, cols)) – Shape of each frame in the resized video. If one of the dimensions is set to -1, its value will be inferred by keeping a constant aspect ratio.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the resized video.

  • **kwargs – Additional keyword arguments passed to skimage.transform.resize, such as order=0 for nearest-neighbor interpolation (which keeps a binary video binary).

Returns:

stim – A copy of the stimulus object containing the resized video

Return type:

VideoStimulus

crop(idx_space=None, idx_time=None, left=0, right=0, top=0, bottom=0, front=0, back=0, electrodes=None)[source]

Crop the video

This method maps a rectangle (defined by two corners) from each video frame to a rectangle of the given size. Similarly, the video can be shortened to a specified range of frames.

Alternatively, this method can be used to crop a number of columns either from the left or the right of the video frame, or a number of rows either from the top or the bottom, or a number of frames from the front (beginning) or back (end) of the video.

Added in version 0.8.

Parameters:
  • idx_space (4-tuple (y0, x0, y1, x1)) – Image indices of the top-left corner [y0, x0] and bottom-right corner [y1, x1] (exclusive) of the rectangle to crop.

  • idx_time (tuple (t0, t1)) – Frame indices defining the start t0 and end t1 of the cropped video.

  • left (int) – Number of columns to crop from the left of each video frame

  • right (int) – Number of columns to crop from the right of each video frame

  • top (int) – Number of rows to crop from the top of each video frame

  • bottom (int) – Number of rows to crop from the bottom of each video frame

  • front (int) – Number of frames to crop from the front (beginning) of the video

  • back (int) – Number of frames to crop from the back (end) of the video

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in the cropped image.

Returns:

stim – A copy of the stimulus object containing the video

Return type:

VideoStimulus

crop_square()[source]

Crop each video frame to the largest centered square

Frames are cropped without resizing. If their dimensions differ by an odd number of pixels, the extra pixel is removed from the bottom or right.

Added in version 0.11.0.

Returns:

stim – A copy of the stimulus containing the centered square crop.

Return type:

VideoStimulus

trim(tol=0, electrodes=None)[source]

Remove any black border around the video

Added in version 0.7.

Parameters:
  • tol (float) – Any pixels with gray levels > tol will be trimmed.

  • electrodes (int, string or list thereof; optional) –

    Optionally, you can provide your own electrode names. If none are given, each pixel is named after its place in the image (e.g. ‘A1’, ‘C12’, ‘A1_R’).

    Note

    The number of electrode names provided must match the number of pixels in each frame of the trimmed video.

Returns:

stim – A copy of the stimulus object with trimmed borders.

Return type:

VideoStimulus

rotate(angle, mode='constant', electrodes=None, **kwargs)[source]

Rotate each frame of the video

Changed in version 0.10.0: Keyword arguments are passed on to scikit-image.

Parameters:
  • angle (float or Quantity) – Angle by which to rotate each video frame (degrees). Positive: counter-clockwise, negative: clockwise

  • mode (str, optional) – How to fill in the corners the rotation leaves empty; see skimage.transform.rotate.

  • electrodes (int, string or list thereof; optional) – Optionally, you can provide your own electrode names. If none are given, each pixel keeps the name it had before the rotation, unless resize=True grew the frame, in which case the enlarged video is named after its own pixel grid.

  • **kwargs – Additional keyword arguments passed to skimage.transform.rotate, such as order, cval, or resize=True to grow each frame so that it contains every rotated pixel.

Returns:

stim – A copy of the stimulus object containing the rotated video

Return type:

VideoStimulus

shift(shift_cols, shift_rows)[source]

Shift the image foreground

This function shifts the center of mass (CoM) of the image by the specified number of rows and columns.

Parameters:
  • shift_cols (float) – Number of columns by which to shift the CoM. Positive: to the right, negative: to the left

  • shift_rows (float) – Number of rows by which to shift the CoM. Positive: downward, negative: upward

Returns:

stim – A copy of the stimulus object containing the shifted image

Return type:

ImageStimulus

center(loc=None)[source]

Center the image foreground

This function shifts the center of mass (CoM) to the image center.

Parameters:

loc ((col, row), optional) – The pixel location at which to center the CoM. By default, shifts the CoM to the image center.

Returns:

stim – A copy of the stimulus object containing the centered image

Return type:

ImageStimulus

scale(scaling_factor)[source]

Scale the image foreground

This function scales the image foreground (excluding black pixels) by a factor.

Parameters:

scaling_factor (float) – Factory by which to scale the image

Returns:

stim – A copy of the stimulus object containing the scaled image

Return type:

ImageStimulus

filter(filt, **kwargs)[source]

Filter each frame of the video

Parameters:
  • filt (str) –

    Image filter that will be applied to every frame of the video. Additional parameters can be passed as keyword arguments. The following filters are supported:

    • ’sobel’: Edge filter the image using the Sobel filter.

    • ’scharr’: Edge filter the image using the Scarr filter.

    • ’canny’: Edge filter the image using the Canny algorithm. You can also specify sigma, low_threshold, high_threshold, mask, and use_quantiles.

    • ’median’: Return local median of the image.

  • **kwargs – Additional parameters passed to the filter

Returns:

stim – A copy of the stimulus object with the filtered image

Return type:

VideoStimulus

encode(amp_range=(0, 50), freq=20, implant=None, **kwargs)[source]

Encode the video using amplitude modulation

Encodes every frame of the video as a train of biphasic pulses, where the gray level of a pixel sets the amplitude of its pulses. Each train lasts one frame period.

This is a shorthand for AmplitudeEncoder; use that directly for the full set of options.

Changed in version 0.10.0: Gray levels now map onto amp_range absolutely rather than being stretched to fill it (pass stretch=True for the old behavior), each frame receives a pulse train rather than a single pulse, and implant encodes at electrode rather than pixel resolution.

Parameters:
  • amp_range ((min_amp, max_amp), optional) – Range of pulse amplitudes (uA). A gray level of 0 maps onto min_amp, a gray level of 1 onto max_amp.

  • freq (float, optional) – Pulse train frequency (Hz).

  • implant (Implant, optional) – If given, the video is first sampled at the implant’s electrode locations, so that the pulse trains are built at electrode rather than pixel resolution. Strongly recommended: a video has orders of magnitude more pixels than an implant has electrodes.

  • **kwargs – Additional arguments passed to AmplitudeEncoder.

Returns:

stim – Encoded stimulus

Return type:

Stimulus

append(other)[source]

Append another stimulus

This method appends another stimulus (with matching electrodes) in time. The combined stimulus duration will be the sum of the two individual stimuli.

Added in version 0.7.

Parameters:

other (Stimulus) – Another stimulus with matching electrodes.

Returns:

comb – A combined stimulus with the same number of electrodes and new stimulus duration equal to the sum of the two individual stimuli.

Return type:

Stimulus

property data

Stimulus data container

A read-only 2-D NumPy array that contains the sampled waveform, where the rows denote electrodes and the columns denote points in time.

property dt

Sampling time step (duration of signal edge transitions)

Added in version 0.7.

property duration

Stimulus duration (ms)

property electrodes

Electrode names A list of electrode names, corresponding to the rows in the data container.

property is_charge_balanced

Flag indicating whether the stimulus is charge-balanced

A stimulus with a time component is considered charge-balanced if its net current is smaller than 10 pico Amps. For the whole stimulus to be charge-balanced, every electrode must be charge-balanced as well.

Changed in version 0.10.0: Returns None for a stimulus that is not measured in units of current.

property is_compressed

Flag indicating whether the stimulus has been compressed

pad(duration)[source]

Pad the stimulus with zeros up to a given time.

Adds zero-valued endpoints at t=0 and t=duration as needed. duration specifies the final time, not the amount of padding to add.

Added in version 0.10.0.

Parameters:

duration (float or Quantity) – Final time of the padded stimulus. Bare values are interpreted in the stimulus’ time unit.

Returns:

padded – Padded copy of the stimulus.

Return type:

Stimulus

Notes

Padding requires existing boundary values to be zero; otherwise interpolation would create a ramp. It never truncates the stimulus or removes negative time points.

play(fps=None, repeat=True, annotate_time=True, ax=None, fmt='jpg')[source]

Animate the video as HTML with JavaScript

The video will be played in an interactive player in IPython or Jupyter Notebook.

Parameters:
  • fps (float or None) – If None, uses the video’s time axis. Not supported for non-homogeneous time axis. May be given as a plain number of hertz or as a unitful frequency (e.g. 30 * Hz, 0.03 * kHz); see pulse2percept.units.

  • repeat (bool, optional) – Whether the animation should repeat when the sequence of frames is completed.

  • annotate_time (bool, optional) – If True, the time of the frame will be shown as t = X ms in the title of the panel.

  • ax (matplotlib.axes.AxesSubplot, optional) – A Matplotlib axes object. If None, will create a new Axes object

  • fmt ({'jpg', 'png'}, optional) –

    The image format used to embed the frames. ‘jpg’ keeps notebooks and doc pages an order of magnitude smaller; use ‘png’ if you need the frames to be pixel-exact.

    Added in version 0.10.0.

Returns:

ani – A Matplotlib animation object that will play the video frame-by-frame.

Return type:

pulse2percept.utils.HTMLAnimation

Notes

Changed in version 0.10.0: The HTML player is now generated by HTMLAnimation, which renders the figure once and ships all frames as a single sprite sheet. This is roughly two orders of magnitude faster than Matplotlib’s to_jshtml and produces much smaller notebooks and doc pages.

plot(electrodes=None, time=None, fmt='k-', ax=None, kind=None)[source]

Plot the stimulus

Added in version 0.7.

Changed in version 0.10.0: Added kind: a whole multi-electrode stimulus is now drawn as an electrode-by-time heatmap rather than as one subplot per electrode.

Parameters:
  • electrodes (int, string, or list thereof; optional, default: None) – The electrodes for which to plot the stimulus. If None, all electrodes are plotted.

  • time ((t_min, t_max) tuple, slice, or list of exact time points) – The time points at which to plot the stimulus. Specify a range of time points with a tuple or a slice, or specify the exact time points to interpolate. If None, all time points are plotted.

  • fmt (str, optional, default: 'k-') – A Matplotlib format string; e.g., ‘ro’ for red circles to use when kind='traces'.

  • ax (matplotlib.axes.Axes or list thereof; optional, default: None) – A Matplotlib Axes object. kind='traces' also accepts a list thereof (one per electrode to plot); kind='heatmap' draws into a single Axes. If None, a new Axes object will be created.

  • kind ({'traces', 'heatmap'}, optional, default: None) –

    What to draw:

    • ’traces’: the waveform of each electrode, one Axes per electrode. Good for a handful of electrodes.

    • ’heatmap’: an electrode-by-time image in a single Axes. Good for a whole implant’s worth of electrodes.

    If None, a whole stimulus of more than one electrode is drawn as a heatmap and everything else as traces.

Returns:

ax – One Axes per electrode for kind='traces', a single Axes for kind='heatmap'.

Return type:

matplotlib.axes.Axes or np.ndarray of them

property quantity

The stimulus data, with its unit attached

Added in version 0.10.0.

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA
>>> Stimulus([500, 1000] * uA).quantity
[[ 500.]
 [1000.]] uA
remove(electrodes)[source]

Remove electrode(s)

Removes the stimulus of a certain electrode or list of electrodes.

Added in version 0.8.

Parameters:

electrodes (int, string, or list of int/str) – The item(s) to remove from the stimulus. Can either be an electrode index, electrode name, or a list thereof.

property shape

Data container shape

property time

A list of time steps (i.e., the columns in the data container)

property time_quantity

The stimulus time axis with its unit attached, or None

Added in version 0.10.0.

property time_unit

The unit time is expressed in

Added in version 0.10.0.

times(unit=None)[source]

The stimulus time axis, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the time axis in. If None, time is returned as it is stored (milliseconds).

Returns:

times – An ordinary NumPy array, or None if the stimulus has no time component.

Return type:

np.ndarray or None

property unit

The unit data is expressed in

Added in version 0.10.0.

values(unit=None)[source]

The stimulus data, expressed in unit

Added in version 0.10.0.

Parameters:

unit (Unit, optional) – The unit to express the data in. Must be compatible with unit. If None, the stimulus’ own unit is used and data is returned as it is stored.

Returns:

values – An ordinary NumPy array, never a Quantity.

Return type:

np.ndarray

Examples

>>> from pulse2percept.stimuli import Stimulus
>>> from pulse2percept.units import uA, mA
>>> Stimulus([500, 1000] * uA).values(mA)
array([[0.5],
       [1. ]], dtype=float32)