Generating animation from image sequence in PsychoPy?

728 views Asked by At

I am new to PsychoPy, having previously worked with Pygame for several months (I switched to enable stimuli to be presented on multiple screens).

I am trying to figure out how to use PsychoPy to display an animation created using a sequence of images. I previously achieved this in Pygame by saving the entire sequence of images in a single large png file (a spritesheet) and then flipping only a fraction of that image (eg. 480 x 480 pixels) per frame, while moving onto the next equally sized section of the image in the next frame. This is roughly what my code looked like in Pygame. I would be really keen to hear if there is an equivalent way of generating animations in PsychoPy by selecting only parts of an image to be displayed with each frame. So far, googling this has not provided any answers!

gameDisplay=pygame.display.set_mode((800, 480))

sequence=pygame.image.load('C:\Users\...\image_sequence.png') 
#This image contains 10 images in a row which I cycle through to get an  animation

image_width=480
image_height=480

start=time.time()
frame_count=0
refresh=0

while time.time()<=start+15:

    gameDisplay.blit(sequence,(160,0),(frame_count*image_width,0,image_width,image_height)) 

    if time.time()>= start+(refresh*0.25): #Flip a new image say every 250 msec

        pygame.display.update()
        frame_count+=1 
        refresh+=1

    if frame_count ==10:
        frame_count=0
2

There are 2 answers

0
Jon On

Actually, given a spritesheet you might be able to do something funky and more efficient using the GratingStim. This loads an image as a texture and then allows you to set the spatial frequncy (sf) and phase of that texture. If 1.0/sf (in both dimensions) is less than the width of the stimulus (in both dimensions) only a fraction of the texture will be shown and the phase determines which fraction that will be. It isn't designed for this purpose - it's usually used to create more than one cycle of texture not less than one - but I think it will work.

0
Jonas Lindeløv On

You could use a square aperture to restrict what's visible and then move the image. So something like this (untested, but could give you some ideas):

from psychopy import visual
win = visual.Window(units='pix')  # easiest to use pixels as unit
aperture = visual.Aperture(win, shape='rect', size=(480, 480))
image = visual.ImageStim('C:\Users\...\image_sequence.png')

# Move through x positions
for x in range(10):
    image.pos = [(-10.0/2*+0.5+x)*480, 0]  # not sure this is right, but it should move through the x-positions
    image.draw()
    win.flip()

If you have the original images, I think that it would be simpler to just display the original images in sequence.

import glob
from psychopy import visual
image_names = glob.glob('C:\Users\...\*.png')

# Create psychopy objects
win = visual.Window()
image_stims = [visual.ImageStim(win, image) for image in image_names]

# Display images one by one
for image in image_stims:
    image.draw()
    win.flip()
    # add more flips here if you want a lower frame rate

Perhaps it is even fast enough to load them during runtime without dropping frames, which would simplify the code and load memory less:

# Imports, glob, and win here
# Create an ImageStim and update the image each frame
stim = visual.ImageStim(win)
for name in image_names:
    stim.image = name
    stim.draw()
    win.flip()