psychopy.org | Reference | Downloads | Github

Advice on using moviestim3

Hi all,
My experiment involves presentation of videos and collecting continuous rating responses. I am using Psychopy2 (v1.84.2) on a Win 7, 64 bit computer (Processor: Intel Xeon CPU E5-2620 v3, 2.40 GHz, RAM: 16 GB, Graphics Card: GeForce GTX 980).

The videos are all 30 seconds long, re-encoded using the same codec (H.264-MPEG-4 AVC) and without audio, all have the same display resolution (1280x720), and they are around 30-40 MB.

I will show ~60 clips in 4 blocks and I wrote the script in a way that only the videos which will be shown in the current block have been loaded into the memory (using a function).
Even then the videos are stuttering time to time and the viewing experience is not that smooth.
I would like to ask some advice from Psychopy community regarding my call to the MovieStim3 or the way that I display the videos. I am sharing the relevant parts down here and would appreciate any suggestion. I would be willing to share any additional information you might ask, too.

#WINDOW SETTINGS
win = visual.Window(size=(1920, 1080),#size=(800,600),
                    screen=0, winType='pyglet',
                    allowGUI=False,  # show mouse
                    fullscr=info['full_screen'],
                    monitor=moni,
                    color='grey',
                    colorSpace='rgb',
                    units='deg',
                    useFBO=True
                    )

#FUNCTION CALL USING MOVIESTIM3
def load_movies(idx_run):
    """Load movies which will be displayed in the current run."""
    global nr_movies, movie_orders, video_paths

    # initiate list of lists to store moviestim
    movies = list()
    [movies.append([]) for m in range(nr_movies)]

    for j in movie_orders[idx_run]:
        print 'Loading movie %i' % j
        movies[j] = visual.MovieStim3(win, video_paths[j],
                                      size=(1280, 720),
                                      pos=[0, 0], flipVert=False,
                                      flipHoriz=False, loop=False,
                                      noAudio=True)
    return movies

#DISPLAY THE MOVIE IN THE RENDER LOOP
if state_ide[count_s] >= 0:
               movies[state_ide[count_s]].draw()

Thanks,
ilkay

Does this get better if you take away the rating scale?
I assume you’re in full screen and you’ve turned off everything else you can.

Yes I am running the experiment in full screen but I want to point out that I am not using visual.RatingStim, I obtain the continuous rating values with a slider bar I created using the Grating.Stim as shown below. I am keeping the tick positions in a list while the movies are shown with the slider bar positioned under the movie.

slider_bar = visual.GratingStim(win=win, tex=None,
                                pos=(0, 0), size=(40+1, 1.5),
                                color='darkgray', interpolate=False)

slider_tick = visual.GratingStim(win=win, tex=None,
                                pos=(0, 0), size=(1, 1.5),
                                color='DarkRed', interpolate=False)

I tried running the experiment while turning the output and slider bar off and it got better. But I definitely have to have them on during the experiment. Any suggestions on what to change?

Thanks again,
Ilkay

Could you send the full code of your render loop. Maybe there’s something in there that is being done innefficiently. By the way it would help if you formatted the code in your post properly to be readable:

Sorry about the formatting, I edited the previous posts.

""" Render loop """

count_run = 0  # default 0
count_s = 0  # to count states within runs, default 0


while count_run < nr_runs:  # loop over runs

    print 'Run counter: %i' % count_run

    # set run specific parameters
    run_ide = run_orders[count_run]  # run identifier
    state_ide = state_orders[run_ide].astype('int')
    state_dur = state_durations[run_ide].astype(np.float)
    movies = load_movies(run_ide)  # might take some time
    run_duration = np.sum(state_dur[count_s::])
    state_start_offset = np.copy(count_s)  # used in re-started runs, mostly 0

    # ask to start run after loading movies
    start_trigger = True
    text.text = 'Press Enter to start Run %i ' % (count_run+1)
    while start_trigger:
        text.draw()
        win.flip()

        for keys in event.getKeys():

            if keys in ['return']:
                start_trigger = False

            elif keys[0] in ['escape', 'q']:
                win.close()
                core.quit()

    # give the system time to settle
    core.wait(0.5)

    # create a clock (start from 0 after each run)
    clock = core.Clock()
    clock.reset()

    while clock.getTime() < run_duration:  # loop over states

        print 'State counter: %i' % count_s

        state_start = clock.getTime()

        # prepare stimuli for the upcoming state
        if state_ide[count_s] >= 0:
            slider_text_r.pos = (22.5, -22.5)
            slider_text_l.pos = (-22.5, -22.5)
            slider_bar.pos = SLIDER_C_POS
            slider_tik.pos = SLIDER_C_POS
            new_tik_pos = slider_tik.pos
            dial_history = [[SLIDER_C_POS[0], 0]]
            dial = True
            print 'State for this movie: %i' % count_s

            elif state_ide[count_s] == -1:
            slider_text_r.pos = (22.5, 0)
            slider_text_l.pos = (-22.5, 0)
            slider_bar.pos = SLIDER_O_POS
            slider_tik.pos = SLIDER_O_POS
            new_tik_pos = slider_tik.pos
            dial_history = [[SLIDER_O_POS[0], 0]]
            dial = True
            
        # rendering (drawing things on the screen)
        while clock.getTime() < np.sum(state_dur[state_start_offset:count_s+1]):

            # determine state draws
            if state_ide[count_s] >= 0:
                movies[state_ide[count_s]].draw()
                slider_bar.draw()
                slider_text_r.draw()
                slider_text_l.draw()
                slider_tik.pos = tick_limits(new_tik_pos)
                slider_tik.draw()


            elif state_ide[count_s] == -1:
                slider_bar.draw()
                slider_text_r.draw()
                slider_text_l.draw()
                slider_tik.pos = tick_limits(new_tik_pos)
                slider_tik.draw()


            elif state_ide[count_s] == -2:
                win.clearBuffer()

            elif state_ide[count_s] == -3:
                text.text = '+'
                text.pos = (0, 0)
                if clock.getTime() % 0.5 < 0.25:
                    text.color = 'darkgray'
                else:
                    text.color = 'lightgray'
                text.draw()

            win.flip()


            if state_ide[count_s] == -1 and dial_click:
                state_dur[count_s] = clock.getTime() - state_start
                dial_click = False
                run_duration = np.sum(state_dur)  # update
                dial = False
                break

            # handle key presses each frame
            for keys in event.getKeys(timeStamped=True):
                if keys[0]in ['escape', 'q']:
                    win.close()
                    # pprint(output_variables)
                    core.quit()

                elif keys[0]in ['left']:
                    new_tik_pos = tuple(np.subtract(slider_tik.pos, TIK_STEP))

                elif keys[0]in ['right']:
                    new_tik_pos = tuple(np.add(slider_tik.pos, TIK_STEP))

        # cleanup operations (including saving) after each state
        if state_ide[count_s] >= 0:
            movies[state_ide[count_s]].pause()
            output_variables['scale_c'].append(dial_history)


        elif state_ide[count_s] == -1:
            output_variables['scale_o'].append(dial_history[-1])

        output_variables['state_durations'].append(state_dur)
        pickle.dump(output_variables, open(pickle_name, 'wb'))

        count_s = count_s + 1
        #print fliprecord

    count_run = count_run + 1
    count_s = 0  # reset states
    win.clearBuffer()
    win.flip()

The code looks good (I’m assuming that the state control stuff doesn’t ever allow 2 movies to be presented, but if it does that would be your problem)

If getting rid of the rating scales fixes the jerkiness then my guess is that the text rendering is the problem. If you switch to using the visual.TextBox rather than visual.TextStim then I expect it will be faster (TextStim is more flexible in rendering different fonts but much slower).

Alternatively you could use BufferImageStim to show effectively a screen shot of your rating scale (any the parts that don’t move) and just add the pointer on afterwards to show its value. But try textBox first because I think that will be the slowest part of your current rendering

Wanted to give another update.:
I tried using TextBox but I kept getting an error related to RGB values (Sorry that I did not copy the exact error) even though I used it like exactly in the demo.
Then I thought if the problem is text rendering I can use shapestim. Since I am only rendering two letters, I created their vertices and formed letters using the shape stim. I have not seen a clear improvement. Therefore, I wanted to ask do you think it is a good idea?

Next I will try to use the BufferImageStim.