pulse2percept.stimuli.VideoStimulus
- 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>
Added in version 0.7.
- Parameters:
source (str, os.PathLike, VideoStimulus, or np.ndarray) –
Path to a video file (
strorpathlib.Path). File types are inferred from the file ending (support types include MP4, AVI, MOV, and GIF). Enforce a specific format viaformat.Changed in version 0.11.0: A
pathlib.Pathis 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
VideoStimulusobject.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 frameas_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] == 0no matter where it was cut from. To shorten a video that is already in memory, and keep its original time stamps, usecrop()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] == 0no matter where it was cut from. To shorten a video that is already in memory, and keep its original time stamps, usecrop()instead.Added in version 0.10.0.
- __init__(source, format=None, resize=None, as_gray=False, electrodes=None, time=None, metadata=None, compress=False, start_time=None, stop_time=None)[source]
Methods
__init__(source[, format, resize, as_gray, ...])append(other)Append another stimulus
apply(func, *args[, electrodes])Apply a function to each frame of the video
center([loc])Center the image foreground
compress()Compress the source data
crop([idx_space, idx_time, left, right, ...])Crop the video
Crop each video frame to the largest centered square
encode([amp_range, freq, implant])Encode the video using amplitude modulation
filter(filt, **kwargs)Filter each frame of the video
invert()Invert the gray levels of the video
pad(duration)Pad the stimulus with zeros up to a given time.
play([fps, repeat, annotate_time, ax, fmt])Animate the video as HTML with JavaScript
plot([electrodes, time, fmt, ax, kind])Plot the stimulus
remove(electrodes)Remove electrode(s)
resize(shape[, electrodes])Resize the video
rgb2gray([electrodes])Convert the video to grayscale
rotate(angle[, mode, electrodes])Rotate each frame of the video
scale(scaling_factor)Scale the image foreground
shift(shift_cols, shift_rows)Shift the image foreground
times([unit])The stimulus time axis, expressed in
unittrim([tol, electrodes])Remove any black border around the video
values([unit])The stimulus data, expressed in
unitAttributes
vid_shapeStimulus data container
Sampling time step (duration of signal edge transitions)
Stimulus duration (ms)
Electrode names A list of electrode names, corresponding to the rows in the data container.
Flag indicating whether the stimulus is charge-balanced
Flag indicating whether the stimulus has been compressed
metadataThe stimulus data, with its unit attached
Data container shape
A list of time steps (i.e., the columns in the data container)
The stimulus time axis with its unit attached, or None
The unit
timeis expressed inThe unit
datais expressed in- compress()[source]
Compress the source data
Also brings
vid_shapeback 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. Everydata.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:
- apply(func, *args, electrodes=None, **kwargs)[source]
Apply a function to each frame of the video
Changed in version 0.10.0:
funcmay now change the shape of a frame, andelectrodescan 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
funcleaves the shape of a frame alone, and the result is named after its place in the new frame otherwise (e.g. forskimage.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=0for 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
t0and endt1of 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=Truegrew 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, orresize=Trueto 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:
- 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, anduse_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_rangeabsolutely rather than being stretched to fill it (passstretch=Truefor the old behavior), each frame receives a pulse train rather than a single pulse, andimplantencodes 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 ontomax_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:
- 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.
- 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=0andt=durationas needed.durationspecifies 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:
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); seepulse2percept.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:
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’sto_jshtmland 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 forkind='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.
- 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
timeis expressed inAdded in version 0.10.0.
- times(unit=None)[source]
The stimulus time axis, expressed in
unitAdded in version 0.10.0.
- Parameters:
unit (
Unit, optional) – The unit to express the time axis in. If None,timeis 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
datais expressed inAdded in version 0.10.0.
- values(unit=None)[source]
The stimulus data, expressed in
unitAdded in version 0.10.0.
- Parameters:
unit (
Unit, optional) – The unit to express the data in. Must be compatible withunit. If None, the stimulus’ own unit is used anddatais 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)