I am try to create a matrix of squares and draw them onto a window in PsychoPy. I have accomplished drawing the grid using the visual.Rect and saving the window onto a visual.BufferedImage, then drawing that. I want to now change the colors of the tiles in the grid to simulate stimuli results. I know that this can be done using visual.GratingStim, but I cannot find any examples of either how to create a grid like structure using GratingStim or examples of GratingStim working with BufferedImage. If anyone has an experience of resources please let me know!
To make a grid, I like to use the visual.ElementArrayStim
, I feel I get more flexibility. Below is an example of how I do that. You can set the colors and the position of the grid by editing the colors
or the location
variables.
from psychopy import visual
import numpy as np
win = visual.Window(units='pix')
num_check = 50
check_size = [10, 10]
location = [0, 0]
# generate loc array
loc = np.array(location) + np.array(check_size) // 2
# array of rgbs for each element (2D)
colors = np.random.random((num_check ** 2, 3))
# array of coordinates for each element
xys = []
# populate xys
low, high = num_check // -2, num_check // 2
for y in range(low, high):
for x in range(low, high):
xys.append((check_size[0] * x,
check_size[1] * y))
stim = visual.ElementArrayStim(win,
xys=xys,
fieldPos=loc,
colors=colors,
nElements=num_check**2,
elementMask=None,
elementTex=None,
sizes=(check_size[0],
check_size[1]))
stim.size = (check_size[0] * num_check,
check_size[1] * num_check)
for i in range(60):
stim.draw()
win.flip()
Thank you so much! This was very helpful! Do you know if this is compatible if i want to use a timestamp when changing the colors of the tiles while the GUI is open?
And if I want the tiles to all be colored in a gray scale, how should I do that instead of using the random that is there?
I’m not familiar with the GUI unfortunately, I only use the coder side of Psychopy. To make a grayscale checkerboard, you can try replacing color
with something like this:
colors = np.stack((np.random.random(num_check ** 2),)*3, -1)
Instead of randomizing the RGB values for each tile, this just picks a single value for each tile and then “stacks” them to replicate the value into the RGB values.
Also what is num_check and check_size, do I adjust these to make the squares smaller/larger?
Exactly! The 4 parameters you can adjust are num_check
, check_size
, location
, and colors
. That should change, respectively, the number of checks in a side of the grid, the size of the checks, where the grid is positioned, and the colors of each check.
Also, if you want to ensure that your random grayscale grid is repeatable, you should seed the random generator. You can do this by adding the following before the colors
line:
np.random.seed(0)
, or any other number of your choice.
I am trying to make the grid a 3X6 so I set num_check to be equal to 18 and check_size to 10,10 and I am getting a large amount of squares in a small grid. Do you have any solution to this? Also thank you so much for your help!
To do different number of checks along each axis, it requires a few changes. num_check
needs to be an iterable. I’ve made the changes in the code below.
from psychopy import visual
import numpy as np
win = visual.Window(units='pix')
# x, y coordinates
num_check = [3, 6]
check_size = [50, 50]
location = [0, 0]
# generate loc array
loc = np.array(location) + np.array(check_size) // 2
# array of rgbs for each element (2D)
colors = np.stack((np.random.random(num_check[0] * num_check[1]),)*3, -1)
# array of sizes for each element
xys = []
# populate xys
low_x, high_x = num_check[0] // -2, num_check[0] // 2
low_y, high_y = num_check[1] // -2, num_check[1] // 2
for y in range(low_y, high_y):
for x in range(low_x, high_x):
xys.append((check_size[0] * x,
check_size[1] * y))
stim = visual.ElementArrayStim(win,
xys=xys,
fieldPos=loc,
colors=colors,
nElements=num_check[0] * num_check[1],
elementMask=None,
elementTex=None,
sizes=(check_size[0],
check_size[1]))
stim.size = (check_size[0] * num_check[0],
check_size[1] * num_check[1])
for i in range(120):
stim.draw()
win.flip()
Hi, may I ask what decides the number in the range()?
for i in range(120):
stim.draw()
win.flip()
I tried to figure it out, but failed. I used the code and the array is not drawn in the center, I’m trying to debug it and wonder this could be the cause?
Thank you a lot!
The number in range just determines for how many frames the stimulus will be drawn. In the case, on a 60 hz monitor, 120 frames will mean the stimulus is drawn for 2 seconds.
Thank you so much
Hi!
I am trying to solve a similar problem. I need the squares of the grid to be blue or orange, in changing proportions. Any ideas how to accomplish this? Thanks!