Source code for stimupy.stimuli.waves

import numpy as np

from stimupy.components import waves
from stimupy.components.shapes import disc, rectangle
from stimupy.stimuli import place_targets

__all__ = [
    "sine_linear",
    "square_linear",
    "staircase_linear",
    "sine_radial",
    "square_radial",
    "staircase_radial",
    "sine_rectilinear",
    "square_rectilinear",
    "staircase_rectilinear",
    "sine_angular",
    "square_angular",
    "staircase_angular",
]


[docs]def sine_linear( visual_size=None, ppd=None, shape=None, frequency=None, n_bars=None, bar_width=None, period="ignore", rotation=0.0, phase_shift=0, intensities=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="corner", round_phase_width=True, ): """Linear (horizontal, vertical, oblique) sine-wave grating, with some phase(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_bars : int, or None (default) number of bars in the grating bar_width : Number, or None (default) width of a single bar, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensities : Sequence[float, float] or None (default) min and max intensity of sine-wave target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of bars given resolution Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ if len(intensities) != 2: raise ValueError("intensities should be [float, float]") lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" ) if rotation % 180 == 0.0: distance_metric = "horizontal" elif rotation % 180 == 90.0: distance_metric = "vertical" else: distance_metric = "oblique" # Spatial square-wave grating stim = waves.sine( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_bars, phase_width=bar_width, period=period, rotation=rotation, phase_shift=phase_shift, intensities=intensities, origin=origin, round_phase_width=round_phase_width, distance_metric=distance_metric, ) # Repackage output stim["n_bars"] = stim.pop("n_phases") stim["bar_width"] = stim.pop("phase_width") stim.pop("distance_metric") # Rename mask stim["bar_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def square_linear( visual_size=None, ppd=None, shape=None, frequency=None, n_bars=None, bar_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_bars=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="corner", round_phase_width=True, ): """Linear (horizontal, vertical, oblique) square-wave grating (set of bars), with some bar(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_bars : int, or None (default) number of bars in the grating bar_width : Number, or None (default) width of a single bar, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_bars : Sequence[float, ...] intensity value for each bar, by default (1.0, 0.0). Can specify as many intensities as n_bars; If fewer intensities are passed than n_bars, cycles through intensities target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of bars given resolution Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" ) # Spatial square-wave grating stim = waves.square( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_bars, phase_width=bar_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="oblique", intensities=intensity_bars, ) # Repackage output stim["n_bars"] = stim.pop("n_phases") stim["bar_width"] = stim.pop("phase_width") stim["intensity_bars"] = stim.pop("intensities") stim.pop("distance_metric") # Rename mask stim["bar_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def staircase_linear( visual_size=None, ppd=None, shape=None, frequency=None, n_bars=None, bar_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_bars=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="corner", round_phase_width=True, ): """Linear staircase, with some bar(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_bars : int, or None (default) number of bars in the grating bar_width : Number, or None (default) width of a single bar, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_bars : Sequence[float, float] or Sequence[float, ...] min and max intensity of sine-wave, by default (0.0, 1.0). Can also specify as many intensities as n_bars target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of bars given resolution intensity_bars : Sequence[float, ...] if len(intensity_bars)==2, intensity range of staircase (default 0.0, 1.0); if len(intensity_bars)>2, intensity value for each bar. Can specify as many intensity_bars as n_bars. If fewer intensity_bars are passed than n_bars, cycles through intensities. Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_bars, bar_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_bars', 'bar_width'" ) # Spatial square-wave grating stim = waves.staircase( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_bars, phase_width=bar_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="oblique", intensities=intensity_bars, ) # Repackage output stim["n_bars"] = stim.pop("n_phases") stim["bar_width"] = stim.pop("phase_width") stim["intensity_bars"] = stim.pop("intensities") stim.pop("distance_metric") # Rename mask stim["bar_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def sine_radial( visual_size=None, ppd=None, shape=None, frequency=None, n_rings=None, ring_width=None, period="ignore", rotation=0.0, phase_shift=0, intensities=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Circular sine-wave grating (set of rings) over the whole image, with some ring(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_rings : int, or None (default) number of rings ring_width : Number, or None (default) width of a single ring, in degrees period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensities : Sequence[float, float] or None (default) min and max intensity of sine-wave target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of rings given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ if len(intensities) != 2: raise ValueError("intensity_rings should be [float, float]") lst = [visual_size, ppd, shape, frequency, n_rings, ring_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_rings', 'rings_width'" ) # Spatial square-wave grating stim = waves.sine( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_rings, phase_width=ring_width, period=period, rotation=rotation, phase_shift=phase_shift, intensities=intensities, origin=origin, round_phase_width=round_phase_width, distance_metric="radial", ) # Repackage output stim["n_rings"] = stim.pop("n_phases") stim["ring_width"] = stim.pop("phase_width") stim.pop("distance_metric") # Clip? if clip: csize = min(stim["visual_size"]) / 2.0 circle = disc( visual_size=stim["visual_size"], ppd=stim["ppd"], radius=csize, origin=origin, ) stim["img"] = np.where(circle["ring_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(circle["ring_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["ring_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def square_radial( visual_size=None, ppd=None, shape=None, frequency=None, n_rings=None, ring_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_rings=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Circular square-wave grating (set of rings) over the whole image, with some ring(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_rings : int, or None (default) number of rings in the grating ring_width : Number, or None (default) width of a single ring, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_rings : Sequence[float, float] intensity value for each ring, by default (1.0, 0.0). target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of rings given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_rings, ring_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_rings', 'ring_width'" ) # Spatial square-wave grating stim = waves.square( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_rings, phase_width=ring_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="radial", intensities=intensity_rings, ) # Repackage output stim["n_rings"] = stim.pop("n_phases") stim["ring_width"] = stim.pop("phase_width") stim["intensity_rings"] = stim.pop("intensities") stim.pop("distance_metric") # Clip? if clip: csize = min(stim["visual_size"]) / 2.0 circle = disc( visual_size=stim["visual_size"], ppd=stim["ppd"], radius=csize, origin=origin, ) stim["img"] = np.where(circle["ring_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(circle["ring_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["ring_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def staircase_radial( visual_size=None, ppd=None, shape=None, frequency=None, n_rings=None, ring_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_rings=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Radial staircase, with some ring(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_rings : int, or None (default) number of rings in the grating ring_width : Number, or None (default) width of a single ring, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_rings : Sequence[float, ...] if len(intensity_rings)==2, intensity range of staircase (default 0.0, 1.0); if len(intensity_rings)>2, intensity value for each ring. Can specify as many intensity_rings as n_rings. If fewer intensity_bars are passed than n_rings, cycles through intensities. target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of rings given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_rings, ring_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_rings', 'ring_width'" ) # Spatial square-wave grating stim = waves.staircase( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_rings, phase_width=ring_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="radial", intensities=intensity_rings, ) # Repackage output stim["n_rings"] = stim.pop("n_phases") stim["ring_width"] = stim.pop("phase_width") stim["intensity_rings"] = stim.pop("intensities") stim.pop("distance_metric") # Clip? if clip: csize = min(stim["visual_size"]) / 2.0 circle = disc( visual_size=stim["visual_size"], ppd=stim["ppd"], radius=csize, origin=origin, ) stim["img"] = np.where(circle["ring_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(circle["ring_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["ring_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def sine_rectilinear( visual_size=None, ppd=None, shape=None, frequency=None, n_frames=None, frame_width=None, period="ignore", rotation=0.0, phase_shift=0, intensities=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Rectilinear sine-wave grating (set of frames) over the whole image, with some frame(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_frames : int, or None (default) number of frames frame_width : Number, or None (default) width of a single frame, in degrees period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensities : Sequence[float, float] or None (default) min and max intensity of sine-wave target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of frames given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ if len(intensities) != 2: raise ValueError("intensity_frames should be [float, float]") lst = [visual_size, ppd, shape, frequency, n_frames, frame_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_frames', 'frames_width'" ) # Spatial square-wave grating stim = waves.sine( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_frames, phase_width=frame_width, period=period, rotation=rotation, phase_shift=phase_shift, intensities=intensities, origin=origin, round_phase_width=round_phase_width, distance_metric="rectilinear", ) # Repackage output stim["n_frames"] = stim.pop("n_phases") stim["frame_width"] = stim.pop("phase_width") stim.pop("distance_metric") # Clip? if clip: if origin == "corner": rsize = min(stim["visual_size"]) / 2 rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, rectangle_position=(0, 0), ) else: rsize = min(stim["visual_size"]) rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, ) stim["img"] = np.where(rect["rectangle_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(rect["rectangle_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["frame_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def square_rectilinear( visual_size=None, ppd=None, shape=None, frequency=None, n_frames=None, frame_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_frames=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Rectilinear square-wave grating (set of frames) over the whole image, with some frame(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_frames : int, or None (default) number of frames in the grating frame_width : Number, or None (default) width of a single frame, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_frames : Sequence[float, float] intensity value for each frame, by default (1.0, 0.0). target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of frames given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_frames, frame_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_frames', 'frame_width'" ) # Spatial square-wave grating stim = waves.square( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_frames, phase_width=frame_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="rectilinear", intensities=intensity_frames, ) # Repackage output stim["n_frames"] = stim.pop("n_phases") stim["frame_width"] = stim.pop("phase_width") stim["intensity_frames"] = stim.pop("intensities") stim.pop("distance_metric") # Clip? if clip: if origin == "corner": rsize = min(stim["visual_size"]) / 2 rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, rectangle_position=(0, 0), ) else: rsize = min(stim["visual_size"]) rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, ) stim["img"] = np.where(rect["rectangle_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(rect["rectangle_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["frame_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def staircase_rectilinear( visual_size=None, ppd=None, shape=None, frequency=None, n_frames=None, frame_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_frames=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, clip=False, intensity_background=0.5, ): """Rectilinear staircase, with some frame(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per degree visual angle n_frames : int, or None (default) number of frames in the grating frame_width : Number, or None (default) width of a single frame, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_frames : Sequence[float, ...] if len(intensity_frames)==2, intensity range of staircase (default 0.0, 1.0); if len(intensity_frames)>2, intensity value for each frame. Can specify as many intensity_frames as n_frames. If fewer intensity_frames are passed than n_frames, cycles through intensities. target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner if "mean": set origin to hypothetical image center (default) if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of frames given resolution clip : Bool if True, clip stimulus to image size (default: False) intensity_background : float (optional) intensity value of background (if clipped), by default 0.5 Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_frames, frame_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_frames', 'frame_width'" ) # Spatial square-wave grating stim = waves.staircase( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_frames, phase_width=frame_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="rectilinear", intensities=intensity_frames, ) # Repackage output stim["n_frames"] = stim.pop("n_phases") stim["frame_width"] = stim.pop("phase_width") stim["intensity_frames"] = stim.pop("intensities") stim.pop("distance_metric") # Clip? if clip: if origin == "corner": rsize = min(stim["visual_size"]) / 2 rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, rectangle_position=(0, 0), ) else: rsize = min(stim["visual_size"]) rect = rectangle( visual_size=stim["visual_size"], ppd=stim["ppd"], rectangle_size=rsize, ) stim["img"] = np.where(rect["rectangle_mask"], stim["img"], intensity_background) stim["grating_mask"] = np.where(rect["rectangle_mask"], stim["grating_mask"], 0) stim["clip"] = clip stim["intensity_background"] = intensity_background # Rename mask stim["frame_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def sine_angular( visual_size=None, ppd=None, shape=None, frequency=None, n_segments=None, segment_width=None, period="ignore", rotation=0.0, phase_shift=0, intensities=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, ): """Angular sine-wave grating (set of segments) over the whole image, with some segment(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per image n_segments : int, or None (default) number of segments segment_width : Number, or None (default) width of a single segment, in degrees period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensities : Sequence[float, float] or None (default) min and max intensity of sine-wave target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of segments given resolution Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ if len(intensities) != 2: raise ValueError("intensity_segments should be [float, float]") lst = [visual_size, ppd, shape, frequency, n_segments, segment_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_segments', 'segments_width'" ) # Spatial square-wave grating stim = waves.sine( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_segments, phase_width=segment_width, period=period, rotation=rotation, phase_shift=phase_shift, intensities=intensities, origin=origin, round_phase_width=round_phase_width, distance_metric="angular", ) # Repackage output stim["n_segments"] = stim.pop("n_phases") stim["segment_width"] = stim.pop("phase_width") stim.pop("distance_metric") # Rename mask stim["segment_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def square_angular( visual_size=None, ppd=None, shape=None, frequency=None, n_segments=None, segment_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_segments=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="mean", round_phase_width=True, ): """Angular square-wave grating (set of segments) over the whole image, with some segment(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per image n_segments : int, or None (default) number of segments in the grating segment_width : Number, or None (default) width of a single segment, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_segments : Sequence[float, ...] intensity value for each segment, by default (1.0, 0.0). Can specify as many intensities as n_segments; If fewer intensities are passed than n_segments, cycles through intensities target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of segments given resolution Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_segments, segment_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'" ) # Spatial square-wave grating stim = waves.square( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_segments, phase_width=segment_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="angular", intensities=intensity_segments, ) # Repackage output stim["n_segments"] = stim.pop("n_phases") stim["segment_width"] = stim.pop("phase_width") stim["intensity_segments"] = stim.pop("intensities") stim.pop("distance_metric") # Rename mask stim["segment_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
[docs]def staircase_angular( visual_size=None, ppd=None, shape=None, frequency=None, n_segments=None, segment_width=None, period="ignore", rotation=0.0, phase_shift=0, intensity_segments=(0.0, 1.0), target_indices=(), intensity_target=0.5, origin="center", round_phase_width=True, ): """Angular staircase, with some segment(s) as target(s) Parameters ---------- visual_size : Sequence[Number, Number], Number, or None (default) visual size [height, width] of image, in degrees ppd : Sequence[Number, Number], Number, or None (default) pixels per degree [vertical, horizontal] shape : Sequence[Number, Number], Number, or None (default) shape [height, width] of image, in pixels frequency : Number, or None (default) spatial frequency of grating, in cycles per image n_segments : int, or None (default) number of segments in the grating segment_width : Number, or None (default) width of a single segment, in degrees visual angle period : "even", "odd", "either" or "ignore" (default) ensure whether the grating has "even" number of phases, "odd" number of phases, either or whether not to round the number of phases ("ignore") rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) phase_shift : float phase shift of grating in degrees intensity_segments : Sequence[float, ...] if len(intensity_segments)==2, intensity range of staircase (default 0.0, 1.0); if len(intensity_segments)>2, intensity value for each segment. Can specify as many intensity_segments as n_segments. If fewer intensity_segments are passed than n_segments, cycles through intensities. target_indices : int, or Sequence[int, ...] indices segments where targets will be placed intensity_target : float, or Sequence[float, ...], optional intensity value for each target, by default 0.5. Can specify as many intensities as number of target_indices; If fewer intensities are passed than target_indices, cycles through intensities origin : "corner", "mean" or "center" if "corner": set origin to upper left corner (default) if "mean": set origin to hypothetical image center if "center": set origin to real center (closest existing value to mean) round_phase_width : Bool if True, round width of segments given resolution Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each target (key: "target_mask"), and additional keys containing stimulus parameters """ lst = [visual_size, ppd, shape, frequency, n_segments, segment_width] if len([x for x in lst if x is not None]) < 3: raise ValueError( "'grating' needs 3 non-None arguments for resolving from 'visual_size', " "'ppd', 'shape', 'frequency', 'n_segments', 'segment_width'" ) # Spatial square-wave grating stim = waves.staircase( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_phases=n_segments, phase_width=segment_width, period=period, rotation=rotation, phase_shift=phase_shift, origin=origin, round_phase_width=round_phase_width, distance_metric="angular", intensities=intensity_segments, ) # Repackage output stim["n_segments"] = stim.pop("n_phases") stim["segment_width"] = stim.pop("phase_width") stim["intensity_segments"] = stim.pop("intensities") stim.pop("distance_metric") # Rename mask stim["segment_mask"] = stim["grating_mask"] # Add targets stim = place_targets( stim=stim, element_mask_key="grating_mask", target_indices=target_indices, intensity_target=intensity_target, ) return stim
def overview(**kwargs): """Generate example stimuli from this module Returns ------- stims : dict dict with all stimuli containing individual stimulus dicts. """ default_params = {"visual_size": 15, "ppd": 30} default_params.update(kwargs) grating_params = { "period": "ignore", "phase_shift": 0, "round_phase_width": False, } grating_params2 = { "period": "ignore", "phase_shift": 0, "round_phase_width": False, "target_indices": (2, 5), } # fmt: off stimuli = { "sine_wave_linear": sine_linear(**default_params, **grating_params, bar_width=1, rotation=45), "sine_wave_radial": sine_radial(**default_params, **grating_params, ring_width=1), "sine_wave_rectilinear": sine_rectilinear(**default_params, **grating_params, frame_width=1), "sine_wave_angular": sine_angular(**default_params, **grating_params, n_segments=10), "square_wave_linear": square_linear(**default_params, **grating_params, bar_width=1, rotation=45), "square_wave_radial": square_radial(**default_params, **grating_params, ring_width=1), "square_wave_rectilinear": square_rectilinear(**default_params, **grating_params, frame_width=1, clip=True), "square_wave_angular": square_angular(**default_params, **grating_params, n_segments=10), "staircase_linear": staircase_linear(**default_params, **grating_params, frequency=0.4), "staircase_radial": staircase_radial(**default_params, **grating_params, frequency=0.4), "staircase_rectilinear": staircase_rectilinear(**default_params, **grating_params, frequency=0.4), "staircase_angular": staircase_angular(**default_params, **grating_params, n_segments=10), "sine_wave_linear_with_targets": sine_linear(**default_params, **grating_params2, bar_width=1, rotation=45), "sine_wave_radial_with_targets": sine_radial(**default_params, **grating_params2, ring_width=1), "sine_wave_rectilinear_with_targets": sine_rectilinear(**default_params, **grating_params2, frame_width=1), "sine_wave_angular_with_targets": sine_angular(**default_params, **grating_params2, n_segments=10), "square_wave_linear_with_targets": square_linear(**default_params, **grating_params2, bar_width=1, rotation=45), "square_wave_radial_with_targets": square_radial(**default_params, **grating_params2, ring_width=1), "square_wave_rectilinear_with_targets": square_rectilinear(**default_params, **grating_params2, frame_width=1, clip=True), "square_wave_angular_with_targets": square_angular(**default_params, **grating_params2, n_segments=10), "staircase_linear_with_targets": staircase_linear(**default_params, **grating_params2, frequency=0.4), "staircase_radial_with_targets": staircase_radial(**default_params, **grating_params2, frequency=0.4), "staircase_rectilinear_with_targets": staircase_rectilinear(**default_params, **grating_params2, frequency=0.4), "staircase_angular_with_targets": staircase_angular(**default_params, **grating_params2, n_segments=10), } # fmt: on return stimuli if __name__ == "__main__": from stimupy.utils import plot_stimuli stims = overview() plot_stimuli(stims, mask=False, save=None)