Image stimulus of staircase experiment does not change

Hi everyone,

I used the coder to build a staircase experiment.
The aim of the experiment is to find out which level of contrast of an image is prefered by the observers.
The code is similar to the one in Tutorial 2.

My problem is, that the code is running throw, but the stimulus (the contrast of the image) does not change. Can you please help me fixing my code?

staircase = data.StairHandler(startVal=1.0, # start value
                             nReversals=3,  # number of reversal points
                             stepSizes= 0.2,# stepsize = single value, list or array
                             nTrials=1,     # minimum number of trials
                             nUp= 1,        # number of ‘incorrect’ (or 0) responses before staircase level increases
                             nDown= 2,      # number of ‘correct’ (or 1) responses before staircase level decreases
                             stepType='lin',# type of steps
                             minVal= 0.2,   # lower limit for value
                             maxVal= 1.0)   # upper limit for value

# create window where stimulus is presented
win = visual.Window([800,600],allowGUI=True, monitor='testMonitor', units='cm')

# create stimulus
img1 = visual.ImageStim(win, image=path, mask=None, pos=[0.0, 0.0], size=10, anchor=True, ori=0.0, color=[1.0, 1.0, 1.0], colorSpace='rgb', contrast=1.0, opacity=None, depth=0, texRes=128)

# display instructions and wait
message1 = visual.TextStim(win, pos=[0,+3],text='Hit a key when ready')
message2 = visual.TextStim(win, pos=[0,-3],
    text="Then press left or right arrow to tell if u like the stimulus better or not - left = no, right = yes")
message1.draw()
message2.draw()

#img1.draw()
win.flip() # to show our newly drawn 'stimuli'
event.waitKeys() # pause until there's a keypress

for thisIncrement in staircase:  # will continue the staircase until it terminates!
    # set contrast of probe
    img1.contrast = (img1.contrast + thisIncrement)

    # draw all stimuli
    img1.draw()
    win.flip()

    # wait 500ms; but use a loop of x frames for more accurate timing
    core.wait(0.5)

    # blank screen
    #img1.draw()
    #win.flip()

    # get response - participant presses left or right arrow
    thisResp=None
    while thisResp==None:
        allKeys=event.waitKeys()
        for thisKey in allKeys:
            if thisKey=='left': thisResp = -1
            elif thisKey=='right': thisResp = 1
            elif thisKey in ['q', 'escape']:
                core.quit()  # abort experiment
    event.clearEvents()  # clear other (eg mouse) events - they clog the buffer

    # add the data to the staircase so it can calculate the next level
    staircase.addData(thisResp)
    dataFile.write('%.3f,%.3f,%i\n' %(img1.contrast, thisIncrement, thisResp))
    core.wait(1)

Fortunately you are logging img1.contrast, thisIncrement, and thisResp, so please show us that output so we can see what your code is actually doing (ideally you would also be logging the keypress).

This is the rest of the code:

# staircase has ended
dataFile.close()
staircase.saveAsPickle(fileName)  # special python binary file to save all the info

# give some output to user in the command line in the output window
print('reversals:')
print(staircase.reversalIntensities)
approxThreshold = np.average(staircase.reversalIntensities[-3:])
print('mean of final 3 reversals = %.3f' % (approxThreshold))

# give some on-screen feedback
feedback1 = visual.TextStim(win, pos=[0,+3],text='mean of final 3 reversals = %.3f' % (approxThreshold))

feedback1.draw()
win.flip()
event.waitKeys()  # wait for participant to respond

win.close()
core.quit()

The desired output during the experiment should be img1 with different contrast levels according to the subjects response, and at the end of the experiment the final output is the mean contrast level of img1 of the 3 reversal points.

During experiment:

After experiment:

i.e. please show us the output that goes into your log file (ideally with the keypresses added) - simply copy and paste the content here. We need to see how the variables are changing with each iteration of the staircase - this should help narrow down the cause of the issue.

Here is what is logged (I used the left and right arrow keys as user response):
image

This output clearly shows how the stimulus contrast isn’t changing as you expect it to.

The initial value is 2.0, although one would expect it to start at 1.0. The reason for this is this initial line:

img1.contrast = (img1.contrast + thisIncrement)

The default value for the stimulus is 1.0 and then you immediately add the starting value of the staircase (1.0) to get to 2.0, before any response has even been made.

The issue here is that you are treating the value of the staircase as an increment, rather than the absolute value of the contrast on the current trial. i.e. don’t add it to the current stimulus contrast value: instead just replace the stimulus contrast value. The staircase value is always positive (as you constrained it to fall between 0.2 and 1.0), so adding it to the current contrast value can only ever result in it increasing monotonically, unbounded.

If you inspect the second column, you’ll see that the actual staircase value is instead going up and down, between the range boundaries of 1.0 and 0.2 that you specified. So use this value.

Secondly, I’m not quite sure about the way you are calculating the response value. You set it to either 1 or -1, but the API says that it expects values of either 0 or 1:

https://psychopy.org/api/data.html#psychopy.data.StairHandler.addResponse

Lastly, I would strongly encourage you to use the Builder to create your experiment rather than code it from scratch. It would avoid problems like this. In this case, you would likely only need to constrain a keyboard component to only accept 'left' or 'right' and then insert 'right' in its “correct answer” field. The staircase loop would then automatically calculate as needed. Builder maintains a variable called level that is the current value of the staircase. To control your image stimulus contrast, you would simply need to put level in its contrast field, and set it to update on every repeat.

Let Builder do the hard work for you, so that you can concentrate on rigorously testing your experiment, rather than just trying to get it to work in the first place.

Hi Michael,

thank you so much for your detailed response!

I’ve tried to used the builder as you recommended it to me, to make it easier for me.
I was already able to fix the issue with user response (set right arrow key as correct answer).

However, writing ‘level’ into the contrast field of my img stimulus field, does not result in a change of the contrast during my experiment. Any ideas why is that the case and how to fix it?

image

It would be useful to try the same approach before, and check the relevant values on each trial.

e.g. insert a code component (from the “Custom” components tab) and in the "End routine tab, put something like this:

print(f'Level: {level}. Correct:{your_keyboard_component_name.corr}.')

This should yield a printout showing how the level variable changes with each response.

You could also check the value of a variable in real-time by simply putting it in a temporary text component. e.g. put $level in its text field, set to update every routine, or for that matter, a more comprehensive expression like the one above:

$f'Level: {level}. Correct:{your_keyboard_component_name.corr}.'

That would give you real-time feedback on how the value is changing (or possibly, not changing).

EDIT:
One thing where I put you wrong was in the “correct answer” field of the keyboard component. It turns out that that doesn’t need quotes after all, and I guess an “incorrect” response is what should make the value go up, so I think that field should just contain left (i.e. without the quotes).

In retrospect, after a quick test, I think the erroneous advice on the keyboard component was what was causing the problem for you.

Thank you so much!
The experiment now runs as desired.