Skip to content

Commit

Permalink
Bullseyes from rings
Browse files Browse the repository at this point in the history
  • Loading branch information
JorisVincent committed Mar 21, 2023
1 parent d977fbb commit f5690c6
Showing 1 changed file with 229 additions and 20 deletions.
249 changes: 229 additions & 20 deletions stimupy/bullseyes.py
@@ -1,7 +1,12 @@
from stimupy.utils import resolution, stack_dicts
from stimupy.waves import square_radial
from stimupy import rings

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


def circular(
Expand Down Expand Up @@ -89,7 +94,7 @@ def circular(
Perception, 34, 557-564.
https://doi.org/10.1068/p5414
"""
stim = square_radial(
stim = rings.circular(
visual_size=visual_size,
ppd=ppd,
shape=shape,
Expand Down Expand Up @@ -190,40 +195,242 @@ def circular_two_sided(
https://doi.org/10.1068/p5414
"""

# Resolve resolution
shape, visual_size, ppd = resolution.resolve(shape=shape, visual_size=visual_size, ppd=ppd)

stim1 = circular(
visual_size=(visual_size[0], visual_size[1] / 2),
stim = rings.circular_two_sided(
visual_size=visual_size,
ppd=ppd,
shape=shape,
frequency=frequency,
n_rings=n_rings,
ring_width=ring_width,
phase_shift=phase_shift,
intensity_target=intensity_target,
intensity_rings=intensity_rings,
intensity_background=intensity_background,
intensity_target=intensity_target,
target_indices=0,
origin=origin,
clip=True,
)
return stim


def rectangular(
visual_size=None,
ppd=None,
shape=None,
frequency=None,
n_frames=None,
frame_width=None,
phase_shift=0,
intensity_frames=(1.0, 0.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
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
"""

stim2 = circular(
visual_size=(visual_size[0], visual_size[1] / 2),
stim = rings.rectangular(
shape=shape,
visual_size=visual_size,
ppd=ppd,
frequency=frequency,
n_rings=n_rings,
ring_width=ring_width,
n_frames=n_frames,
frame_width=frame_width,
phase_shift=phase_shift,
intensity_frames=intensity_frames,
target_indices=0,
intensity_target=intensity_target,
intensity_rings=intensity_rings[::-1],
origin=origin,
clip=clip,
intensity_background=intensity_background,
)
return stim


def rectangular_generalized(
visual_size=None,
ppd=None,
shape=None,
radii=None,
intensity_frames=(1.0, 0.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
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,
intensity_frames=intensity_frames,
intensity_background=intensity_background,
target_indices=1,
intensity_target=intensity_target,
origin=origin,
clip=True,
)
return stim


def rectangular_two_sided(
visual_size=None,
ppd=None,
shape=None,
frequency=None,
n_frames=None,
frame_width=None,
phase_shift=0,
intensity_target=0.5,
intensity_frames=(1.0, 0.0),
intensity_background=0.5,
origin="mean",
):
"""Two-sided 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
phase_shift : float
phase shift of grating in degrees
period : "full", "half", "ignore" (default)
whether to ensure the grating only has "full" periods,
half "periods", or no guarantees ("ignore")
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
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
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)
stim = stack_dicts(stim1, stim2)
stim["shape"] = shape
stim["visual_size"] = visual_size
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_two_sided(
visual_size=visual_size,
ppd=ppd,
shape=shape,
frequency=frequency,
n_frames=n_frames,
frame_width=frame_width,
phase_shift=phase_shift,
intensity_frames=intensity_frames,
intensity_background=intensity_background,
intensity_target=intensity_target,
target_indices=0,
origin=origin,
)
return stim


Expand All @@ -245,6 +452,8 @@ def overview(**kwargs):
stimuli = {
"circular": circular(**default_params, frequency=1.0, clip=True),
"circular, two sided": circular_two_sided(**default_params, frequency=1.0),
"rectangular": rectangular(**default_params, frequency=1.0, clip=True),
"rectangular, two sided": rectangular_two_sided(**default_params, frequency=1.0),
}
# fmt: on

Expand Down

0 comments on commit f5690c6

Please sign in to comment.