# Source code for psychopy.visual.filters

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Various useful functions for creating filters and textures
(e.g. for PatchStim)
"""

# Part of the PsychoPy library
# Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2022 Open Science Tools Ltd.

import numpy
from numpy.fft import fft2, ifft2, fftshift, ifftshift

[docs]def makeGrating(res, ori=0.0, # in degrees cycles=1.0, phase=0.0, # in degrees gratType="sin", contr=1.0): """Make an array containing a luminance grating of the specified params :Parameters: res: integer the size of the resulting matrix on both dimensions (e.g 256) ori: float or int (default=0.0) the orientation of the grating in degrees cycles:float or int (default=1.0) the number of grating cycles within the array phase: float or int (default=0.0) the phase of the grating in degrees (NB this differs to most PsychoPy phase arguments which use units of fraction of a cycle) gratType: 'sin', 'sqr', 'ramp' or 'sinXsin' (default="sin") the type of grating to be 'drawn' contr: float (default=1.0) contrast of the grating :Returns: a square numpy array of size resXres """ # to prevent the sinusoid ever being exactly at zero (for sqr wave): tiny = 0.0000000000001 ori *= -numpy.pi / 180. phase *= numpy.pi / 180. cyclesTwoPi = cycles * 2.0 * numpy.pi xrange, yrange = numpy.mgrid[ 0.0:cyclesTwoPi:(cyclesTwoPi / res), 0.0:cyclesTwoPi:(cyclesTwoPi / res)] sin, cos = numpy.sin, numpy.cos if gratType == "none": res = 2 intensity = numpy.ones((res, res), float) elif gratType == "sin": intensity = contr * sin(xrange * sin(ori) + yrange * cos(ori) + phase) elif gratType == "ramp": intensity = contr * (xrange * cos(ori) + yrange * sin(ori)) / (2 * numpy.pi) elif gratType == "sqr": # square wave (symmetric duty cycle) intensity = numpy.where(sin(xrange * sin(ori) + yrange * cos(ori) + phase + tiny) >= 0, 1, -1) elif gratType == "sinXsin": intensity = sin(xrange) * sin(yrange) else: # might be a filename of an image # try: # im = Image.open(gratType) # except Exception: # logging.error("couldn't find tex...", gratType) # return # # todo: opened it, now what? raise ValueError("Invalid value for parameter gratType.") return intensity
[docs]def maskMatrix(matrix, shape='circle', radius=1.0, center=(0.0, 0.0)): """Make and apply a mask to an input matrix (e.g. a grating) :Parameters: matrix: a square numpy array array to which the mask should be applied shape: 'circle','gauss','ramp' (linear gradient from center) shape of the mask radius: float scale factor to be applied to the mask (circle with radius of [1,1] will extend just to the edge of the matrix). Radius can be asymmetric, e.g. [1.0,2.0] will be wider than it is tall. center: 2x1 tuple or list (default=[0.0,0.0]) the centre of the mask in the matrix ([1,1] is top-right corner, [-1,-1] is bottom-left) """ # NB makeMask now returns a value -1:1 alphaMask = makeMask(matrix.shape[0], shape, radius, center=(0.0, 0.0), range=[0, 1]) return matrix * alphaMask
[docs]def makeRadialMatrix(matrixSize, center=(0.0, 0.0), radius=1.0): """Generate a square matrix where each element values is its distance from the centre of the matrix. Parameters ---------- matrixSize : int Matrix size. Corresponds to the number of elements along each dimension. Must be >0. radius: float scale factor to be applied to the mask (circle with radius of [1,1] will extend just to the edge of the matrix). Radius can be asymmetric, e.g. [1.0,2.0] will be wider than it is tall. center: 2x1 tuple or list (default=[0.0,0.0]) the centre of the mask in the matrix ([1,1] is top-right corner, [-1,-1] is bottom-left) Returns ------- ndarray Square matrix populated with distance values and size == (matrixSize, matrixSize). """ if type(radius) in [int, float]: radius = [radius, radius] try: matrixSize = int(matrixSize) except ValueError: raise TypeError('parameter matrixSize must be a numeric type') if matrixSize <= 1: raise ValueError( 'parameter matrixSize must be positive and greater than 1, got: {}'.format( matrixSize)) # NB need to add one step length because yy, xx = numpy.mgrid[0:matrixSize, 0:matrixSize] xx = ((1.0 - 2.0 / matrixSize * xx) + center[0]) / radius[0] yy = ((1.0 - 2.0 / matrixSize * yy) + center[1]) / radius[1] rad = numpy.sqrt(numpy.power(xx, 2) + numpy.power(yy, 2)) return rad