Source code for psychos.visual.gabor

from typing import Tuple, Optional, Union, Callable, Literal, TYPE_CHECKING


from .raw_image import RawImage
from .synthetic import gabor_3d


if TYPE_CHECKING:
    import numpy as np

    from ..types import ColorType, UnitType
    from .window import Window
    from .units import Unit
    


[docs] class Gabor(RawImage): """ A Gabor stimulus for psychophysical experiments. This class generates a synthetic Gabor patch using a 3D (color) Gabor generator and displays it in a Pyglet window via the RawImage (Sprite) component. It combines the parameters for generating the Gabor stimulus with those for positioning, scaling, and rotating the image. Parameters ---------- # Gabor 3D parameters: size : Tuple[int, int], default=(128, 128) The (height, width) of the generated image in pixels. spatial_frequency : float, default=5.0 Cycles per entire image width. orientation : float, default=45.0 Gabor orientation in degrees [0, 360]. The carrier is rotated by (360 - orientation) degrees. phase : float, default=0.0 Fraction of 2π for the carrier phase, in the range [0, 1]. sigma : Optional[float], default=0.15 Normalized standard deviation (fraction of the minimum dimension) of the Gaussian envelope. If None, no envelope is applied (i.e. the envelope defaults to ones). gamma : float, default=1.0 Aspect ratio (vertical stretch) of the Gaussian envelope. contrast : float, default=1.0 Contrast (amplitude) of the sinusoidal carrier. center : Optional[Tuple[float, float]], default=None Center (y, x) of the Gabor patch in pixel coordinates. Defaults to the image center if None. cmap : Union[str, Callable[[np.ndarray], np.ndarray], Tuple[ColorType, ColorType], list], default=("black", "white") Colormap used to map the normalized grayscale Gabor to colors. If a string, it must be a valid matplotlib colormap name. Alternatively, a callable mapping an array in [0,1] to an RGBA image, or a tuple/list of two colors (low and high) for linear two-color interpolation (colors are parsed via Color().to_rgb()). alpha_channel : Union[Literal["envelope"], None, np.ndarray], default="envelope" Determines the alpha channel: - "envelope": use the Gaussian envelope computed in gabor_3d. - None: do not include an alpha channel (output will be RGB). - A numpy array: must have the same shape as the 2D gabor; used as the alpha channel. **kwargs Additional keyword arguments to be passed to the gabor_3d function. # RawImage parameters: image_path : Optional[str] The path to the image file. (Not used here since the image is generated synthetically.) position : Tuple[float, float], default=(0, 0) The position of the image in the window. width : Optional[float], default=None The target width to scale the image to. height : Optional[float], default=None The target height to scale the image to. scale : Optional[float], default=None A uniform scaling factor for the image. rotation : float, default=0 The rotation angle of the image in degrees. anchor_x : str, default="center" The horizontal anchor alignment for the image. anchor_y : str, default="center" The vertical anchor alignment for the image. window : Optional[Window], default=None The window in which the image will be displayed. coordinates : Optional[Union[UnitType, Units]], default=None The coordinate system to use for positioning the image. **kwargs (additional) Additional keyword arguments are passed to the underlying Pyglet Sprite class. Attributes ---------- Inherits all attributes from RawImage, such as position, rotation, scale, etc. Example ------- >>> from synthetic_stim import Gabor # or your module name >>> # Create a Gabor stimulus with a viridis colormap and envelope as the alpha channel. >>> gabor = Gabor( ... size=(256, 256), ... spatial_frequency=2.0, ... orientation=45.0, ... phase=0.25, ... sigma=0.25, ... gamma=1.0, ... contrast=1.0, ... center=(128, 128), ... cmap='viridis', ... alpha_channel="envelope", ... position=(100, 150), ... width=300, ... rotation=30 ... ) >>> gabor.draw() """
[docs] def __init__( self, # Gabor 3D parameters: size: Tuple[int, int] = (256, 256), spatial_frequency: float = 8.0, orientation: float = 45.0, phase: float = 0.0, sigma: Optional[float] = 0.15, gamma: float = 1.0, contrast: float = 1.0, center: Optional[Tuple[float, float]] = None, cmap: Union[ str, Callable[["np.ndarray"], "np.ndarray"], Tuple["ColorType", "ColorType"], list ] = ("black", "white"), alpha_channel: Union[Literal["envelope"], None, "np.ndarray"] = "envelope", gabor_kwargs={}, image_path: Optional[str] = None, position: Tuple[float, float] = (0, 0), width: Optional[float] = None, height: Optional[float] = None, scale: Optional[float] = None, rotation: float = 0, anchor_x: str = "center", anchor_y: str = "center", window: Optional["Window"] = None, coordinates: Optional[Union["UnitType", "Unit"]] = None, **sprite_kwargs, ): # Generate synthetic Gabor image data (scaled to 0-255) data = 255 * gabor_3d( size=size, spatial_frequency=spatial_frequency, orientation=orientation, phase=phase, sigma=sigma, gamma=gamma, contrast=contrast, center=center, cmap=cmap, alpha_channel=alpha_channel, **gabor_kwargs, ) data = data.astype(int) # Initialize the RawImage with the generated data super().__init__( raw_image=data, position=position, width=width, height=height, scale=scale, rotation=rotation, anchor_x=anchor_x, anchor_y=anchor_y, window=window, coordinates=coordinates, **sprite_kwargs, )