многоугольник психопи поверх изображения

Используя психопи версии 1.81.03 на Mac, я хочу нарисовать многоугольник (например, треугольник) поверх изображения. Пока что мое изображение всегда остается сверху и, таким образом, скрывает многоугольник, независимо от того, в каком порядке я их помещаю. Это также остается верным, если у меня многоугольник начинается на кадр позже, чем изображение.

например см. приведенный ниже код (созданный с помощью Builder перед компиляцией), как синий квадрат и красный треугольник должны начинаться с кадра 0, но когда вы запускаете его, синий квадрат всегда закрывает красный треугольник!?

Есть ли способ, чтобы полигон был сверху? Нужно ли как-то объединять изображение и полигон перед их рисованием?

Спасибо большое за вашу помощь!!

Себастьян

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
This experiment was created using PsychoPy2 Experiment Builder (v1.81.03), Sun Jan 18 20:44:26 2015
If you publish work using this script please cite the relevant PsychoPy publications
  Peirce, JW (2007) PsychoPy - Psychophysics software in Python. Journal of Neuroscience Methods, 162(1-2), 8-13.
  Peirce, JW (2009) Generating stimuli for neuroscience using PsychoPy. Frontiers in Neuroinformatics, 2:10. doi: 10.3389/neuro.11.010.2008
"""

from __future__ import division  # so that 1/3=0.333 instead of 1/3=0
from psychopy import visual, core, data, event, logging, sound, gui
from psychopy.constants import *  # things like STARTED, FINISHED
import numpy as np  # whole numpy lib is available, prepend 'np.'
from numpy import sin, cos, tan, log, log10, pi, average, sqrt, std, deg2rad, rad2deg, linspace, asarray
from numpy.random import random, randint, normal, shuffle
import os  # handy system and path functions

# Ensure that relative paths start from the same directory as this script
_thisDir = os.path.dirname(os.path.abspath(__file__))
os.chdir(_thisDir)

# Store info about the experiment session
expName = u'test_triangle_over_square'  # from the Builder filename that created this script
expInfo = {'participant':'', 'session':'001'}
dlg = gui.DlgFromDict(dictionary=expInfo, title=expName)
if dlg.OK == False: core.quit()  # user pressed cancel
expInfo['date'] = data.getDateStr()  # add a simple timestamp
expInfo['expName'] = expName

# Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
filename = _thisDir + os.sep + 'data/%s_%s_%s' %(expInfo['participant'], expName, expInfo['date'])

# An ExperimentHandler isn't essential but helps with data saving
thisExp = data.ExperimentHandler(name=expName, version='',
    extraInfo=expInfo, runtimeInfo=None,
    originPath=None,
    savePickle=True, saveWideText=True,
    dataFileName=filename)
#save a log file for detail verbose info
logFile = logging.LogFile(filename+'.log', level=logging.EXP)
logging.console.setLevel(logging.WARNING)  # this outputs to the screen, not a file

endExpNow = False  # flag for 'escape' or other condition => quit the exp

# Start Code - component code to be run before the window creation

# Setup the Window
win = visual.Window(size=(1280, 800), fullscr=True, screen=0, allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', useFBO=True,
    )
# store frame rate of monitor if we can measure it successfully
expInfo['frameRate']=win.getActualFrameRate()
if expInfo['frameRate']!=None:
    frameDur = 1.0/round(expInfo['frameRate'])
else:
    frameDur = 1.0/60.0 # couldn't get a reliable measure so guess

# Initialize components for Routine "trial"
trialClock = core.Clock()
ISI = core.StaticPeriod(win=win, screenHz=expInfo['frameRate'], name='ISI')
square = visual.ImageStim(win=win, name='square',units='pix', 
    image=None, mask=None,
    ori=0, pos=[0, 0], size=[200, 200],
    color=u'blue', colorSpace='rgb', opacity=1,
    flipHoriz=False, flipVert=False,
    texRes=128, interpolate=True, depth=-1.0)
polygon = visual.ShapeStim(win=win, name='polygon',units='pix', 
    vertices = [[-[200, 300][0]/2.0,-[200, 300][1]/2.0], [+[200, 300][0]/2.0,-[200, 300][1]/2.0], [0,[200, 300][1]/2.0]],
    ori=0, pos=[0, 0],
    lineWidth=1, lineColor=[1,1,1], lineColorSpace='rgb',
    fillColor=u'red', fillColorSpace='rgb',
    opacity=1,interpolate=True)

# Create some handy timers
globalClock = core.Clock()  # to track the time since experiment started
routineTimer = core.CountdownTimer()  # to track time remaining of each (non-slip) routine 

#------Prepare to start Routine "trial"-------
t = 0
trialClock.reset()  # clock 
frameN = -1
# update component parameters for each repeat
# keep track of which components have finished
trialComponents = []
trialComponents.append(ISI)
trialComponents.append(square)
trialComponents.append(polygon)
for thisComponent in trialComponents:
    if hasattr(thisComponent, 'status'):
        thisComponent.status = NOT_STARTED

#-------Start Routine "trial"-------
continueRoutine = True
while continueRoutine:
    # get current time
    t = trialClock.getTime()
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame

    # *square* updates
    if frameN >= 0 and square.status == NOT_STARTED:
        # keep track of start time/frame for later
        square.tStart = t  # underestimates by a little under one frame
        square.frameNStart = frameN  # exact frame index
        square.setAutoDraw(True)

    # *polygon* updates
    if frameN >= 0 and polygon.status == NOT_STARTED:
        # keep track of start time/frame for later
        polygon.tStart = t  # underestimates by a little under one frame
        polygon.frameNStart = frameN  # exact frame index
        polygon.setAutoDraw(True)
    # *ISI* period
    if t >= 0.0 and ISI.status == NOT_STARTED:
        # keep track of start time/frame for later
        ISI.tStart = t  # underestimates by a little under one frame
        ISI.frameNStart = frameN  # exact frame index
        ISI.start(0.5)
    elif ISI.status == STARTED: #one frame should pass before updating params and completing
        ISI.complete() #finish the static period

    # check if all components have finished
    if not continueRoutine:  # a component has requested a forced-end of Routine
        routineTimer.reset()  # if we abort early the non-slip timer needs reset
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in trialComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished

    # check for quit (the Esc key)
    if endExpNow or event.getKeys(keyList=["escape"]):
        core.quit()

    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()
    else:  # this Routine was not non-slip safe so reset non-slip timer
        routineTimer.reset()

#-------Ending Routine "trial"-------
for thisComponent in trialComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
win.close()
core.quit()

person Sebastian Korb    schedule 18.01.2015    source источник
comment
Просто быстрый комментарий: вам не нужно объединять два стимула. И если бы вы использовали код, вы могли бы управлять слоями в порядке, в котором вы вызываете .draw(). Например. square.draw();polygon.draw();win.flip() помещает сверху квадрат, а polygon.draw();square.draw();win.flip() помещает сверху многоугольник. Но билдер работает с использованием атрибута autoDraw, и это может быть причиной проблем здесь, а если так, то это ошибка в программном обеспечении. Я могу изучить это позже. Но квадрат — это просто многоугольник с 4 ребрами, поэтому попробуйте использовать его вместо ImageStim в качестве предварительного обходного пути.   -  person Jonas Lindeløv    schedule 18.01.2015
comment
Если вы используете код, я имею в виду, если вы написали свой эксперимент в коде вместо использования интерфейса компоновщика. Планирование эксперимента с использованием Builder и манипулирование кодом не рекомендуется, поскольку это улица с односторонним движением.   -  person Jonas Lindeløv    schedule 18.01.2015


Ответы (2)


Согласно комментарию Джонаса выше, PsychoPy использует систему слоев, в которой последующие стимулы рисуются поверх предыдущих стимулов (как в его примерах кода).

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

Вы можете изменить порядок компонентов стимула, щелкнув их правой кнопкой мыши и выбрав «Переместить вверх», «Переместить вниз» и т. д. по мере необходимости.

Однако Себастьян обнаружил здесь ошибку, заключающуюся в том, что предполагаемый порядок прорисовки не соблюдается между компонентами ImageStim и ShapeStim. В качестве обходного пути вы можете заменить свой ShapeStim растровым представлением, отображаемым с помощью ImageStim. Несколько ImageStim должны рисовать правильно (как и несколько ShapeStim). Чтобы он правильно рисовал поверх другого изображения, обязательно сохраните его как файл .png, который поддерживает прозрачность. Таким образом, поверх будет нарисована только фактическая фигура, так как ее фоновые пиксели могут быть установлены прозрачными и не будут маскировать основное изображение.

В качестве долгосрочного решения я добавил вашу проблему в качестве отчета об ошибке в проект PsychoPy GitHub здесь: https://github.com/psychopy/psychopy/issues/795

person Michael MacAskill    schedule 19.01.2015
comment
спасибо Джонас и Майкл. при инвертировании порядка компонентов стимула в Билдере, и выставлении треугольника на первое место, ничего не меняется, т.е. синий квадрат по прежнему сверху (собственно насколько я понимаю это не удивительно, но получение того же результата при наличии сначала квадрата должно быть) - person Sebastian Korb; 19.01.2015
comment
Но при использовании 2-х полигонов (вместо изображения и полигона) для рисования квадрата и треугольника работает нормально. на этот раз самый нижний элемент по вертикальной оси Builder отображается сверху, как и предполагалось. Я предполагаю, что есть ошибка с элементом изображения, по крайней мере, при использовании конструктора? - person Sebastian Korb; 19.01.2015
comment
Да, Себастьян, вы правы, похоже, вы нашли ошибку в том, как взаимодействуют эти стимулы. Я отредактировал свой ответ выше, чтобы отразить, что общий метод управления порядком рисования в настоящее время не работает в этой ситуации и что отчет об ошибке был отправлен. - person Michael MacAskill; 19.01.2015

Оказалось, что это ошибка в компоненте Polygon в Builder.

Это исправлено в следующем выпуске (1.82.00). Изменения, необходимые для исправления, можно увидеть по адресу https://github.com/psychopy/psychopy/commit/af1af9a7a85cee9b4ec8ad5e2ff1f03140bd1a36, который вы можете добавить к своей собственной установке, если хотите.

привет, Джон

person Jon    schedule 26.01.2015