pulse2percept.stimuli.TraceEncoder

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)
__init__(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]

Methods

__init__(model, *[, amp, freq, phase_dur, ...])

electrode_sequence(source, *[, extent])

Return the electrodes a target stimulates, in order

encode(source, *[, extent])

Encode a traced target as sequential pulse trains

trajectory(source, *[, extent])

Return the ordered visual-field trajectory of a target

Attributes

model

amp

amp_unit

freq

phase_dur

step_dur

interphase_dur

cathodic_first

clock

region

threshold

prune_spurs

implant

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

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)