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
VisualFieldMapand implant placement to stimulate one electrode at a time, in trajectory order, forstep_dureach: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 onImplant.encoder; callencode()directly.Accepted targets:
An
(N, 2)array of ordered(x, y)positions in dva.A grayscale
ImageStimulusof a single bright stroke on a dark background (e.g., a letter), placed in the visual field byextent. Pixels abovethresholdare skeletonized to a one-pixel centerline, which must form one open, nonbranching path. Withprune_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. Usetrajectory()to check the direction, or pass an(N, 2)trajectory to set it.
Candidates are activated electrodes whose placed location has a finite
to_dvalocation, 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 invertiblevisual_field_map(from_dvaandto_dva), and placed electrode coordinates (implant_position,implant_rotation,implant_depth). The encoder binds tomodel.implant.amp (float or Quantity, optional) – Pulse amplitude (uA, or
xThfor 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
AmplitudeEncoderwithframe_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.
DynaphosModelsimulates an encoded stimulus at its ownfreqandp_dur. Setfreqandphase_durto match them.Models with
location_noiseare 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
modelampamp_unitfreqphase_durstep_durinterphase_durcathodic_firstclockregionthresholdprune_spursThe 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:
source – Target, as in
trajectory().extent – Target, as in
trajectory().
- Returns:
names – One electrode name per
step_durstep, with consecutive duplicates collapsed.- Return type:
- encode(source, *, extent=None)[source]
Encode a traced target as sequential pulse trains
- Parameters:
source – Target, as in
trajectory().extent – Target, as in
trajectory().
- Returns:
stim – Pulse trains in uA (or
xTh), onestep_durstep per selected electrode, lastingn_steps * step_durms. Rows are the visited electrodes in electrode-array order.- Return type:
- property implant
The implant this encoder is bound to, or None (read-only)