Export frames to images

Hi,

Very new to PsychoPy. I’m trying to export every frame within my looped routine as an image (preferably jpeg) so that I can upload the task to Mturq. I tried entering code into the “each frame” tab on the code component in builder, but it doesn’t seem to be working. Any idea what the best/simplest line of code to use for this would be? Do I need to add anything else?

Thank you!!

You really should show us what you did, and what “it doesn’t seem to be working” means.

But regardless, this is the function you should be using to take a screenshot on every frame:

https://www.psychopy.org/api/visual/window.html#psychopy.visual.Window.getMovieFrame

And this function to later save them all to disk in one go:

https://www.psychopy.org/api/visual/window.html#psychopy.visual.Window.saveMovieFrames

Thanks for you help!

I entered win.getMovieFrame() into the “every frame” tab and win.saveMovieFrames(fileName=‘jpgs/stimuli.jpg’) into the “end routine” tab.

Unfortunately, this made the task run weirdly (timing off, some frames missing) and saved ~80 jpgs instead of 5 (I only have 5 conditions). I was hoping it would save one image for every condition. Any idea what I’m doing wrong?

For reference, here is the code that builder spits out:

-------Run Routine “Slide1”-------

while continueRoutine and routineTimer.getTime() > 0:
    # get current time
    t = Slide1Clock.getTime()
    tThisFlip = win.getFutureFlipTime(clock=Slide1Clock)
    tThisFlipGlobal = win.getFutureFlipTime(clock=None)
    frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
    # update/draw components on each frame
    
    # *polygon_2* updates
    if polygon_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        polygon_2.frameNStart = frameN  # exact frame index
        polygon_2.tStart = t  # local t and not account for scr refresh
        polygon_2.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(polygon_2, 'tStartRefresh')  # time at next scr refresh
        polygon_2.setAutoDraw(True)
    if polygon_2.status == STARTED:
        # is it time to stop? (based on global clock, using actual start)
        if tThisFlipGlobal > polygon_2.tStartRefresh + 8-frameTolerance:
            # keep track of stop time/frame for later
            polygon_2.tStop = t  # not accounting for scr refresh
            polygon_2.frameNStop = frameN  # exact frame index
            win.timeOnFlip(polygon_2, 'tStopRefresh')  # time at next scr refresh
            polygon_2.setAutoDraw(False)
    
    # *image* updates
    if image.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        image.frameNStart = frameN  # exact frame index
        image.tStart = t  # local t and not account for scr refresh
        image.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(image, 'tStartRefresh')  # time at next scr refresh
        image.setAutoDraw(True)
    if image.status == STARTED:
        # is it time to stop? (based on global clock, using actual start)
        if tThisFlipGlobal > image.tStartRefresh + 8-frameTolerance:
            # keep track of stop time/frame for later
            image.tStop = t  # not accounting for scr refresh
            image.frameNStop = frameN  # exact frame index
            win.timeOnFlip(image, 'tStopRefresh')  # time at next scr refresh
            image.setAutoDraw(False)
    
    # *image_2* updates
    if image_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        image_2.frameNStart = frameN  # exact frame index
        image_2.tStart = t  # local t and not account for scr refresh
        image_2.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(image_2, 'tStartRefresh')  # time at next scr refresh
        image_2.setAutoDraw(True)
    if image_2.status == STARTED:
        # is it time to stop? (based on global clock, using actual start)
        if tThisFlipGlobal > image_2.tStartRefresh + 8-frameTolerance:
            # keep track of stop time/frame for later
            image_2.tStop = t  # not accounting for scr refresh
            image_2.frameNStop = frameN  # exact frame index
            win.timeOnFlip(image_2, 'tStopRefresh')  # time at next scr refresh
            image_2.setAutoDraw(False)
    
    # *Text2* updates
    if Text2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
        # keep track of start time/frame for later
        Text2.frameNStart = frameN  # exact frame index
        Text2.tStart = t  # local t and not account for scr refresh
        Text2.tStartRefresh = tThisFlipGlobal  # on global time
        win.timeOnFlip(Text2, 'tStartRefresh')  # time at next scr refresh
        Text2.setAutoDraw(True)
    if Text2.status == STARTED:
        # is it time to stop? (based on global clock, using actual start)
        if tThisFlipGlobal > Text2.tStartRefresh + 8-frameTolerance:
            # keep track of stop time/frame for later
            Text2.tStop = t  # not accounting for scr refresh
            Text2.frameNStop = frameN  # exact frame index
            win.timeOnFlip(Text2, 'tStopRefresh')  # time at next scr refresh
            Text2.setAutoDraw(False)
    win.getMovieFrame()''' 

-------Ending Routine “Slide1”-------

for thisComponent in Slide1Components:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
trials_2.addData('polygon_2.started', polygon_2.tStartRefresh)
trials_2.addData('polygon_2.stopped', polygon_2.tStopRefresh)
trials_2.addData('image.started', image.tStartRefresh)
trials_2.addData('image.stopped', image.tStopRefresh)
trials_2.addData('image_2.started', image_2.tStartRefresh)
trials_2.addData('image_2.stopped', image_2.tStopRefresh)
trials_2.addData('Text2.started', Text2.tStartRefresh)
trials_2.addData('Text2.stopped', Text2.tStopRefresh)

win.saveMovieFrames(fileName='jpgs/stimuli.jpg')
thisExp.nextEntry()

completed 1 repeats of ‘trials_2’

This is why it is important to be very precise about descriptions of what you want to achieve on online forums like this: answers will be be in response to what you write, not what you wish for :wink:

But if what you want is just one frame per routine, rather than every frame, just put in a check so that the frame grabbing only happens on, say, the first frame:

# check the frame counter:
if frameN == 0:
    # so that this only happens once per routine:
    win.getMovieFrame()

I was actually hoping to get an image for every condition, not every routine. I have 5 conditions looped in one routine. If I set FrameN == 0, I just get one image from my routine. Is there a way to capture one image for every condition?

Trials:

Just try it and see. The frame counter resets every time the routine runs. I was assuming that the condition changes on each iteration of the loop (and that seems to be the case). So with the code above, the number of screenshots you get won’t be equal to 1, but rather equal to the number of iterations in the trials loop (which looks like it would be 5).

Dear Michael,

I’m also rather new to PsychoPy, and I’m facing the same problem. My experiment look quite similar to the one mentioned in this thread, therefore I tried inserting (I’m using the Builder) the code you suggested (win.getMovieFrame() in Each Frame Tab; win.saveMovieFrames() in End Routine Tab), but I can’t get all the screenshots the loop produces (5), just the first.
I’m using PsychoPy 3.2.4, on Windows 7.

Thank you.

OK, that seems odd. Insert some (temporary) debugging code so we can track how many times this code gets called:

# check the frame counter:
if frameN == 0:
    # so that this only happens once per routine:
    win.getMovieFrame()
    # check how many times this actually occurs
    print(your_loop_name.thisN)