刺激随着每个帧的显示而变化。

时间:2016-05-05 11:31:38

标签: psychopy

我有一些代码(显示如下),可以显示10帧的刺激。我们需要相当精确的显示时间,因此使用帧数是必须的而不是core.wait(xx),因为显示时间不会那么精确。

不是绘制刺激,而是将其留下9帧 - 而是为每一帧重新绘制刺激。

# Import what is needed
import numpy as np
from psychopy import visual, event, core, logging
from math import sin, cos
import random, math

win = visual.Window(size=(1366, 768), fullscr=True, screen=0, allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', useFBO=True,
    units='deg')

### Definitions of libraries
'''Parameters :
    numpy       - python package of numerical computations
    visual      - where all visual stimulus live
    event       - code to deal with mouse + keyboard input
    core        - general function for timing & closing the program
    logging     - provides function for logging error and other messages to one file
    random      - options for creating arrays of random numbers
    sin & cos   - for geometry and trigonometry
    math        - mathematical operations  '''

# this is supposed to record all frames
win.setRecordFrameIntervals(True)
win._refreshThreshold=1/65.0+0.004 #i've got 65Hz monitor and want to allow 4ms tolerance
#set the log module to report warnings to the std output window (default is errors only)
logging.console.setLevel(logging.WARNING)

nIntervals=5

# Create space variables and a window
lineSpaceX = 0.55
lineSpaceY = 0.55

patch_orientation = 45 # zero is vertical, going anti-clockwise
surround_orientation = 90

#Jitter values
g_posJitter = 0.05 #gaussian positional jitter
r_posJitter = 0.05 #random positional jitter

g_oriJitter = 5 #gaussian orientation jitter
r_oriJitter = 5 #random orientation jitter

#create a 1-Dimentional array
line = np.array(range(38)) #with values from (0-37) #possibly not needed 01/04/16 DK

#Region where the rectangular patch would appear
#x_rand=random.randint(1,22) #random.randint(Return random integers from low (inclusive) to high (exclusive).
#y_rand=random.randint(1,25)


x_rand=random.randint(6,13) #random.randint(Return random integers from low (inclusive) to high (inclusive).
y_rand=random.randint(6,16)

#rectangular patch dimensions
width=15
height=12

message = visual.TextStim(win,pos=(0.0,-12.0),text='...Press SPACE to continue...')
fixation = visual.TextStim(win, pos=(0.0,0.0), text='X')

# Initialize clock to record response time
rt_clock = core.Clock()


#Nested loop to draw anti-aliased lines on grid
#create a function for this
def myStim():
    for x in xrange(1,33): #32x32 grid. When x is 33 will not execute loop - will stop
        for y in xrange(1,33): #When y is 33 will not execute loop - will stop
            ##Define x & y value (Gaussian distribution-positional jitter)
            x_pos = (x-32/2-1/2 )*lineSpaceX + random.gauss(0,g_posJitter) #random.gauss(mean,s.d); -1/2 is to center even-numbered stimuli; 32x32 grid
            y_pos = (y-32/2-1/2 )*lineSpaceY + random.gauss(0,g_posJitter)

            if (x >= x_rand and x < x_rand+width) and (y >= y_rand and y < y_rand+height): # note only "=" on one side
                Line_Orientation = random.gauss(patch_orientation,g_oriJitter) #random.gauss(mean,s.d) - Gaussian func.
            else:
                Line_Orientation = random.gauss(surround_orientation,g_oriJitter) #random.gauss(mean,s.d) - Gaussian func.
                #Line_Orientation = random.gauss(Line_Orientation,g_oriJitter) #random.gauss(mean,s.d) - Gaussian func.
                #stimOri = random.uniform(xOri - r_oriJitter, xOri + r_oriJitter) #random.uniform(A,B) - Uniform func.
            visual.Line(win, units = "deg", start=(0,0), end=(0.0,0.35), pos=(x_pos,y_pos), ori=Line_Orientation, autoLog=False).draw() #Gaussian func.



for frameN in range (10):
    myStim()
    win.flip()

print x_rand, y_rand 
print keys, rt  #display response and reaction time on screen output window

我尝试使用以下代码来显示它(通过不清除缓冲区)。但它只是画了好几次。

for frameN in range(10):
    myStim()
    win.flip(clearBuffer=False) 

我意识到问题可能是因为我在我定义的.draw()函数中有def myStim():。但是,如果我在函数中不包含.draw(),我将无法显示刺激。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

如果我理解正确,你面临的问题是你必须在每次翻转时重新绘制刺激,但你当前的绘图功能也会重建整个(随机)刺激,所以:

  • 刺激会在翻转之间的每次抽奖中发生变化,尽管你需要保持不变,
  • 通过一遍又一遍地重新创建整个刺激来获得(在某些系统上非常庞大)性能损失。

你想要的是:在呈现之前,完整地创建一次刺激;然后在每次翻转时都会产生这种预先产生的刺激。

由于您的刺激包含相当多的视觉元素,我建议使用一个类将刺激存储在一个地方

基本上,您将用此类替换myStim()函数(请注意,我删除了大多数注释,重新对齐了一些代码,并简化了if语句):

class MyStim(object):
    def __init__(self):
        self.lines = []

        for x in xrange(1, 33):
            for y in xrange(1, 33):
                x_pos = ((x - 32 / 2 - 1 / 2) * lineSpaceX +
                         random.gauss(0,  g_posJitter))
                y_pos = ((y - 32 / 2 - 1 / 2) * lineSpaceY +
                         random.gauss(0, g_posJitter))

                if ((x_rand <= x < x_rand + width) and
                        (y_rand <= y < y_rand + height)):
                    Line_Orientation = random.gauss(patch_orientation,
                                                    g_oriJitter)
                else:
                    Line_Orientation = random.gauss(surround_orientation,
                                                    g_oriJitter)

                current_line = visual.Line(
                    win, units="deg", start=(0, 0), end=(0.0, 0.35),
                    pos=(x_pos, y_pos), ori=Line_Orientation,
                    autoLog=False
                )

                self.lines.append(current_line)

    def draw(self):
        [line.draw() for line in self.lines]

此代码对实例化的作用原则上与您的myStim()函数相同:它创建了一组(随机)行。但不是立即将它们绘制到屏幕上,而是将它们全部收集到列表self.lines中,并保留在那里,直到我们确实需要它们为止。

draw()方法逐个元素地遍历此列表(即逐行),并调用每一行的draw()方法。请注意,每次我们想要绘制整个集合时都不必重新创建刺激,而是只绘制已经预先创建的行!

要实现这一点,首先需要实例化MyStim类:

myStim = MyStim()

然后,每当你想要提出刺激时,你所要做的就是

myStim.draw()
win.flip()

以下是应该让您入门的完整修改后的代码:

import numpy as np
from psychopy import visual, event, core, logging
from math import sin, cos
import random, math

win = visual.Window(size=(1366, 768), fullscr=True, screen=0, allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', useFBO=True,
    units='deg')

# this is supposed to record all frames
win.setRecordFrameIntervals(True)
win._refreshThreshold=1/65.0+0.004 #i've got 65Hz monitor and want to allow 4ms tolerance
#set the log module to report warnings to the std output window (default is errors only)
logging.console.setLevel(logging.WARNING)

nIntervals=5

# Create space variables and a window
lineSpaceX = 0.55
lineSpaceY = 0.55

patch_orientation = 45 # zero is vertical, going anti-clockwise
surround_orientation = 90

#Jitter values
g_posJitter = 0.05 #gaussian positional jitter
r_posJitter = 0.05 #random positional jitter

g_oriJitter = 5 #gaussian orientation jitter
r_oriJitter = 5 #random orientation jitter

x_rand=random.randint(6,13) #random.randint(Return random integers from low (inclusive) to high (inclusive).
y_rand=random.randint(6,16)

#rectangular patch dimensions
width=15
height=12

message = visual.TextStim(win,pos=(0.0,-12.0),text='...Press SPACE to continue...')
fixation = visual.TextStim(win, pos=(0.0,0.0), text='X')

# Initialize clock to record response time
rt_clock = core.Clock()


class MyStim(object):
    def __init__(self):
        self.lines = []

        for x in xrange(1, 33):
            for y in xrange(1, 33):
                x_pos = ((x - 32 / 2 - 1 / 2) * lineSpaceX +
                         random.gauss(0,  g_posJitter))
                y_pos = ((y - 32 / 2 - 1 / 2) * lineSpaceY +
                         random.gauss(0, g_posJitter))

                if ((x_rand <= x < x_rand + width) and
                        (y_rand <= y < y_rand + height)):
                    Line_Orientation = random.gauss(patch_orientation,
                                                    g_oriJitter)
                else:
                    Line_Orientation = random.gauss(surround_orientation,
                                                    g_oriJitter)

                current_line = visual.Line(
                    win, units="deg", start=(0, 0), end=(0.0, 0.35),
                    pos=(x_pos, y_pos), ori=Line_Orientation,
                    autoLog=False
                )

                self.lines.append(current_line)

    def draw(self):
        [line.draw() for line in self.lines]


myStim = MyStim()
for frameN in range(10):
    myStim.draw()
    win.flip()

# Clear the screen
win.flip()
print x_rand, y_rand

core.quit()

请注意,即使采用这种方法,我也会在具有相对较弱的集成图形芯片的3年前的笔记本电脑上丢帧。但我怀疑现代的,快速的GPU能够处理这么多的视觉对象。在最坏的情况下,您可以预先创建一大组刺激,通过win.saveMovieFrames()将它们保存为位图文件,并在实际学习期间将它们显示为预先加载的SimpleImageStim