Psychopy useful functions
The following document contains a collection of functions created to run experiments in Psychopy.
Creating coloured gratings (pseudo-Gabor).
from psychopy import visual, event, monitors, tools
from psychopy.visual import filters
from psychopy.tools import monitorunittools as mut
import numpy as np
import math
import os
###############################################################################
# VARIABLES
###############################################################################
= True #Set to true if you want to output an image
SAVE_IMAGE = True #Should the stimuli have different luminance?
DIFFERENT_BRIGHTNESS = True #Invert the left and right grating
SWITCH_SIDE = "right" #Either "both", "left" or "rigth"
WHAT_TO_DRAW # Parameters for the gratings
= {
param_stim "resolution": 5, #Size of the stimulus grating in deg (this will be coverted in pix later)
"mask_resolution": 2**11, #Resolution of the mask used to render the gratings as circles (must be a power of 2)
"ori_left": 45, #Orientation of the first grating
"ori_right": -45, #Orientation of the second grating
"pos_left": (0, 0), #Position of the first grating (0,0 is the center of the screen)
"pos_right": (0, 0), #Position of the second grating (0,0 is the center of the screen)
"cycles": 4*5, #Spatial frequency of the gratings. This should be resolution X cycles per deg
"vergence_cycles": 5, #Spatial frequency of the gratings used to create the vergence patterns
"vergence_sf": 0.03, #This value controls the number of gratings used in the vergence patterns (use values < 0.5)
"alpha_left": 1,
"max_value_first": -0.3 #Red: Psychopy [-0.0030, -1,-1], RGB [89,0,0], HSV[0,100,35]
}
# Screen and window parameters - for Psychopy
= {
param_pc "resolution": (1920, 1080),
"width": 34.2}
# Directories and file names for the image output
= os.path.dirname(os.path.abspath(__file__))
this_dir = "red_single_03-1-1.png" #Name of the file to output at the end
image_name
###############################################################################
# WINDOW
###############################################################################
# Create monitor and windows
= monitors.Monitor(
mon ="desk_monitor",
name=param_pc["width"],
width=57
distance
)= param_pc["resolution"]
mon.setSizePix
= visual.Window(
win =param_pc["resolution"],
size=mon,
monitor="pix",
units=False,
allowGUI=1,
screen=False,
fullscr=(-1, -1, -1),
color='rgb',
colorSpace='avg',
blendMode='pyglet',
winType=True)
useFBO
###############################################################################
# FROM DEG TO PIX
###############################################################################
# Convert to pix
"resolution"] = int(mut.deg2pix(param_stim["resolution"], mon))
param_stim[# Round pix to the closest power of 2. NOTE this works for "low" values but
# cannot be generalized to high values (eg. 100000). However, here we work with
# values in the 100 range (eg. 256 pix).
"resolution"] = 2**round(math.log2(param_stim["resolution"]))
param_stim[
###############################################################################
# STIMULI
# To create the gratings we start by creating a black texture defined as a
# matrix of dimension [dim1, dim2, 3], where the three layers represent the RGB
# colours. Then, we will replace the layer of the colour we are interested in
# with a grating, which is a [dim1, dim2] array, conatining values from -1 to 1
# representing the intensity of the colour. Doing this will create a grating
# stimulus of the desired colour.
# For the red stimulusg, we are interested in manipulating its brightness. To do
# so, we define a colour in HSV space and convert it into RGB (use tool online)
# Then we modify the grating range, so that it goes from -1 (black) to N, where
# N is the values obtained online
###############################################################################
# Switch side if requested
if SWITCH_SIDE:
= param_stim["pos_left"]
pos_left = param_stim["pos_right"]
pos_right "pos_left"] = pos_right
param_stim["pos_right"] = pos_left
param_stim[
# ---Coloured Gabors---#
# Create a black texture for both stimuli...
= np.ones((param_stim["resolution"], param_stim["resolution"], 3)) * -1
grating_left = np.ones((param_stim["resolution"], param_stim["resolution"], 3)) * -1
grating_right
# GREEN --> For the green stimulus we simply overaly the grating to the G channel
1] = filters.makeGrating(res=param_stim["resolution"],
grating_right[:, :, =param_stim["ori_right"],
ori=param_stim["cycles"],
cycles="sin")
gratType
# RED --> For the red stimulus we need to do some more work...
# Create a grating
= filters.makeGrating(res=param_stim["resolution"],
sin_mask =param_stim["ori_left"],
ori=param_stim["cycles"],
cycles="sin")
gratType
# If different luminance is requested
if DIFFERENT_BRIGHTNESS:
# Scale only positive values to change the colour (RED) but not the black through the Rohan's transform
# NOTE: it's not a real transform...it was a tip from a friend
= 0.5*(param_stim["max_value_first"]+1)
scale_factor = scale_factor * (sin_mask + 1) - 1
sin_mask_scaled
0] = sin_mask_scaled
grating_left[:,:,# If no difference in brightness is required, apply the grating as above
else:
0] = sin_mask
grating_left[:, :,
#---Vergence Gratings---#
# Create a gray texture (Psychopy [0,0,0] is gray)...
= np.zeros((param_stim["resolution"], param_stim["resolution"], 3))
grating_vergence
#...then overimpose a grid on all the three RGB channels
0] = filters.makeGrating(res=param_stim["resolution"],
grating_vergence[:, :, =param_stim["vergence_cycles"],
cycles=45,
ori='sin')
gratType1] = filters.makeGrating(res=param_stim["resolution"],
grating_vergence[:, :, =param_stim["vergence_cycles"],
cycles=45,
ori='sin')
gratType2] = filters.makeGrating(res=param_stim["resolution"],
grating_vergence[:, :, =param_stim["vergence_cycles"],
cycles=45,
ori='sin')
gratType
#---Circle Mask---#
# Generate a nice smooth (at least almost) circle mask
= filters.makeMask(matrixSize=param_stim["mask_resolution"],
mask ="circle")
shape
#---Generate Stimuli with Psychopy---#
# left grating stimulus
= visual.GratingStim(
stim_left ="stimL",
name=win,
win=(param_stim["resolution"], param_stim["resolution"]),
size=param_stim["pos_left"],
pos=grating_left,
tex=mask,
mask="pix")
units# Right grating stimulus
= visual.GratingStim(
stim_right ="stimR",
name=win,
win=(param_stim["resolution"], param_stim["resolution"]),
size=param_stim["pos_right"],
pos=grating_right,
tex=mask,
mask="pix")
units# Left vergence pattern
= visual.GratingStim(
vergence_left ="vergL",
name=win,
win=(param_stim["resolution"]+50, param_stim["resolution"]+50),
size=param_stim["pos_left"],
pos=grating_vergence,
tex=mask,
mask="pix",
units=param_stim["vergence_sf"])
sf# Right vergence pattern
= visual.GratingStim(
vergence_right ="vergR",
name=win,
win=(param_stim["resolution"]+50, param_stim["resolution"]+50),
size=param_stim["pos_right"],
pos=grating_vergence,
tex=mask,
mask="pix",
units=param_stim["vergence_sf"])
sf
# Left fixation dot
= visual.ShapeStim(
fixation_left =win,
win='polygon',
name=(param_stim["resolution"]/50, param_stim["resolution"]/50),
size='circle',
vertices=0.0,
ori=param_stim["pos_left"],
pos='center',
anchor=1.0,
lineWidth='rgb',
colorSpace='white',
lineColor='white',
fillColor=None,
opacity=0.0,
depth=True)
interpolate
= visual.ShapeStim(
fixation_right =win,
win='polygon',
name=(param_stim["resolution"]/50, param_stim["resolution"]/50),
size='circle',
vertices=0.0,
ori=param_stim["pos_right"],
pos='center',
anchor=1.0,
lineWidth='rgb',
colorSpace='white',
lineColor='white',
fillColor=None,
opacity=0.0,
depth=True)
interpolate
###############################################################################
# DRAW
###############################################################################
if WHAT_TO_DRAW == "both":
# Draw stimuli on buffer
vergence_left.draw()
vergence_right.draw()
stim_left.draw()
stim_right.draw()
fixation_left.draw()
fixation_right.draw()elif WHAT_TO_DRAW == "left":
vergence_left.draw()
stim_left.draw()
fixation_left.draw()else:
vergence_right.draw()
stim_right.draw()
fixation_right.draw()
# Present stimuli on the window
win.flip()# Save stimuli if requested
if SAVE_IMAGE:
= win.getMovieFrame()
frame
frame.save(os.path.join(this_dir, image_name))# Terminate when a key is pressed
event.waitKeys()# Close the Psychopy window
win.close()