Source code for stimupy.stimuli.bullseyes

from stimupy.stimuli import rings
from stimupy.utils import make_two_sided

__all__ = [
    "circular",
    "circular_generalized",
    "circular_two_sided",
    "rectangular",
    "rectangular_generalized",
    "rectangular_two_sided",
]


[docs]def circular( visual_size=None, ppd=None, shape=None, frequency=None, n_rings=None, ring_width=None, phase_shift=0, intensity_target=0.5, intensity_rings=(0.0, 1.0), intensity_background=0.5, origin="mean", clip=True, ): """Circular Bullseye stimulus Circular grating, where the target is the central disc. Alias for circular_white(target_indices=0,...) Specification of the number of rings, and their width can be done in two ways: a ring_width (in degrees) and n_rings, and/or by specifying the spatial frequency of a circular grating (in cycles per degree) The total shape (in pixels) and visual size (in degrees) has to match the specification of the rings and their widths. Thus, not all 6 parameters (visual_size, ppd, shape, frequency, ring_width, n_rings) have to be specified, as long as both the resolution, and the distribution of rings, can be resolved. Note: all rings in a grating have the same width -- if more control is required see disc_and_rings 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 circular grating, in cycles per degree n_rings : int, or None (default) number of rings ring_width : Number, or None (default) width of a single ring, in degrees phase_shift : float phase shift of grating in degrees intensity_target : float (optional) intensity value of target ring(s), by default 0.5 intensity_rings : Sequence[Number, ...] intensity value for each ring, from inside to out, by default [1,0] If fewer intensities are passed than number of radii, cycles through intensities intensity_background : float (optional) intensity value of background, by default 0.5 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) clip : Bool if True, clip stimulus to image size (default: True) 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 References ---------- Bindman, D., & Chubb, C. (2004). Brightness assimilation in bullseye displays. Vision Research, 44, 309-319. https://doi.org/10.1016/S0042-6989(03)00430-9 Hong, S. W., and Shevell, S. K. (2004). Brightness contrast and assimilation from patterned inducing backgrounds. Vision Research, 44, 35-43. https://doi.org/10.1016/j.visres.2003.07.010 Howe, P. D. L. (2005). White's effect: removing the junctions but preserving the strength of the illusion. Perception, 34, 557-564. https://doi.org/10.1068/p5414 """ stim = rings.circular( visual_size=visual_size, ppd=ppd, shape=shape, frequency=frequency, n_rings=n_rings, ring_width=ring_width, phase_shift=phase_shift, intensity_rings=intensity_rings, intensity_background=intensity_background, intensity_target=intensity_target, target_indices=1, origin=origin, clip=clip, ) return stim
[docs]def circular_generalized( visual_size=None, ppd=None, shape=None, radii=None, intensity_rings=(0.0, 1.0), intensity_background=0.5, intensity_target=0.5, origin="mean", ): """Draw sequential set of circular rings with specified radii, with central target 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 radii : Sequence[Number] or None (default) radii of each ring, in degrees visual angle rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) intensity_rings : Sequence[float, float] intensities of rings, by default (1.0, 0.0) intensity_background : float (optional) intensity value of background, by default 0.5 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) Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each frame (key: "target_mask"), and additional keys containing stimulus parameters """ stim = rings.circular_generalized( radii=radii, visual_size=visual_size, ppd=ppd, shape=shape, intensity_rings=intensity_rings, intensity_background=intensity_background, target_indices=1, intensity_target=intensity_target, origin=origin, ) return stim
circular_two_sided = make_two_sided( circular, two_sided_params=("intensity_rings", "intensity_target"), )
[docs]def rectangular( visual_size=None, ppd=None, shape=None, frequency=None, n_frames=None, frame_width=None, rotation=0.0, phase_shift=0, intensity_frames=(0.0, 1.0), intensity_background=0.5, intensity_target=0.5, origin="mean", clip=True, ): """Square "bullseye", i.e., set of rings with target in center Essentially frames(target_indices=1) 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 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] min and max intensity of square-wave, by default (0.0, 1.0) intensity_background : float (optional) intensity value of background, by default 0.5 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) clip : Bool if True, clip stimulus to image size (default: True) 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 References ---------- Bindman, D., & Chubb, C. (2004). Brightness assimilation in bullseye displays. Vision Research, 44, 309-319. https://doi.org/10.1016/S0042-6989(03)00430-9 """ stim = rings.rectangular( shape=shape, visual_size=visual_size, ppd=ppd, frequency=frequency, n_frames=n_frames, frame_width=frame_width, rotation=rotation, phase_shift=phase_shift, intensity_frames=intensity_frames, target_indices=1, intensity_target=intensity_target, origin=origin, clip=clip, intensity_background=intensity_background, ) return stim
[docs]def rectangular_generalized( visual_size=None, ppd=None, shape=None, radii=None, rotation=0.0, intensity_frames=(0.0, 1.0), intensity_background=0.5, intensity_target=0.5, origin="mean", ): """Draw sequential set of square frames with specified radii with central target Parameters ---------- frame_radii : Sequence[Number] radii of each frame, in degrees visual angle 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 radii : Sequence[Number] or None (default) radii of each frame, in degrees visual angle rotation : float, optional rotation (in degrees), counterclockwise, by default 0.0 (horizontal) intensity_frames : Sequence[float, float] min and max intensity of square-wave, by default (0.0, 1.0) intensity_background : float (optional) intensity value of background, by default 0.5 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) Returns ------- dict[str, Any] dict with the stimulus (key: "img"), mask with integer index for each frame (key: "target_mask"), and additional keys containing stimulus parameters """ stim = rings.rectangular_generalized( radii=radii, visual_size=visual_size, ppd=ppd, shape=shape, rotation=rotation, intensity_frames=intensity_frames, intensity_background=intensity_background, target_indices=1, intensity_target=intensity_target, origin=origin, ) return stim
rectangular_two_sided = make_two_sided( rectangular, two_sided_params=("intensity_frames", "intensity_target"), ) def overview(**kwargs): """Generate example stimuli from this module Returns ------- stims : dict dict with all stimuli containing individual stimulus dicts. """ default_params = { "visual_size": (10, 10), "ppd": 30, } default_params.update(kwargs) # fmt: off stimuli = { "bullseyes_circular": circular(**default_params, frequency=1.0, clip=True), "bullseyes_circular_2sided": circular_two_sided(**default_params, frequency=1.0, intensity_rings=((0.0, 1.0), (1.0, 0.0))), "bullseyes_rectangular": rectangular(**default_params, frequency=1.0, clip=True), "bullseyes_rectangular_2sided": rectangular_two_sided(**default_params, frequency=1.0, intensity_frames=((0.0, 1.0), (1.0, 0.0))), } # fmt: on return stimuli if __name__ == "__main__": from stimupy.utils import plot_stimuli stims = overview() plot_stimuli(stims, mask=False, save=None)