2016-05-05 4 views
0

У меня есть немного кода (отображается ниже), который должен отображать стимул для 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

Если я правильно понимаю, проблема вы столкнулись в том, что вы должны перерисовать стимул на каждом флип, но текущая функция рисования также воссоздает весь (случайный) раздражитель, так:

  • стимул изменяется на каждой ничьей между флипами, хотя вам нужно, чтобы он оставался постоянным, и
  • вы получаете (на некоторых системах довольно массивный) штраф за выполнение, повторно создавая весь стимул снова и снова.

Вы хотите вместо этого: создать стимул один раз, полностью, перед представлением; и затем этот заранее сформированный стимул нарисован на каждом перевороте.

Поскольку ваш стимул состоит из довольно большого количества визуальных элементов, я бы предложил использовать класс для хранения стимула в одном месте.

По сути, вы бы заменить 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 во время вашего фактического исследования.

+0

Большое спасибо за вашу помощь. Я даже не знал о классе, пока не прочитал ваше сообщение. Вы помогли сделать это понятным для меня, поэтому еще раз спасибо. –

+0

Можно было бы достичь аналогичного поведения, например, обычные функции и списки * вне * класса; но я верю в конкретную настройку, с которой мы имеем дело, классы - это лучший и понятный подход, поскольку класс * инкапсулирует * данные (строки, которые нужно нарисовать) и соответствующие функции для обработки этих данных ('draw()' метод) в одном месте. – hoechenberger

+0

Не могли бы вы объяснить, почему вы используете 'def draw (self): [line.draw() для строки в self.lines]'? Разве это обычно не как 'for x in y'' x.draw() ' Еще раз спасибо –

 Смежные вопросы

  • Нет связанных вопросов^_^