Preloading images on Builder?

OS (e.g. Win10): Windows 10
PsychoPy version (e.g. 1.84.x): 3.1.2
Standard Standalone? (y/n) If not then what?: Not sure

What are you trying to achieve?:
I want to present a stream of 9 images per trial. I’m drawing images from an Excel file containing ~2000 image filepaths using Builder. However, when I run the experiment the first image of every trial either doesn’t show up, or it’s presented much quicker than the other images. This didn’t happen when I was drawing from a much smaller Excel file containing only 20 images.

What did you try to make it work?:
I want to try preloading the images for each trial prior to that trial starting. I realize you can use the Static component but I’m not sure what to do with it. Any other suggestions/solutions would be appreciated!

Please show us the layout of your trial routine(s). and the flow panel.

e.g. if there is something like a fixation stimulus that appears before the image in each trial, then pre-loading is very easy in Builder, without requiring any custom code.

I’ve attached a screenshot of the trial routine & flow panel. I’ve included a fixation cross routine that comes before the trial routine - what would be the best way of preloading trial images?

psychopy|690x388

Don’t think I replied to you before - I’ve attached a screenshot of the trial routine & flow panel. I’ve included a fixation cross routine that comes before the trial routine - what would be the best way of preloading trial images? I’ve tried just inserting a Static component to different places prior to the image presentations (not all at once) but it still doesn’t work.

OK, so it seems that this is an RSVP paradigm so the images flow on consecutively from each other. That means we can’t use the trick of pre-loading during a fixation point, as that would only work for the first image in the queue. In this case, I’d suggest that you insert a second image stimulus, say RSVPStream_2 into this routine. What we’ll do is alternate which one gets displayed on each iteration of the loop. While one is being displayed, the other gets updated. Because reading in the file for the next trial might take more than one screen refresh period, you should specify the duration here in time (i.e. seconds) rather than frames. That way, the overall duration should be constant for each stimulus, even if a frame gets dropped due to importing the next image.

  • So for each of the two stimuli, specify a fixed image file to use, and set the image field to be “constant” rather than to update on every repeat (because you will need to handle the updating yourself in code, and you don’t want any conflicts).
  • The tricky thing with doing RSVP in Builder is that you don’t know what the first image will be until the loop starts, so that makes it difficult to pre-load the very first image. So you need to do something tricky: shift the routine containing the fixation target inside imageLoop so that we can set the very first image before the images actually start being shown, so it is ready in time. But we don’t want that fixation target to be shown again inside that loop, so insert a code component on that routine and put this in its “Begin routine” tab so that this routine will only appear once:
# run this routine only before the very first image:
if not imageLoop.thisN == 0:
    continueRoutine = False
else:
    # set the first image:
    RSVPStream.image = your_image_name_variable

So that should mean the first image is cued up, before it is scheduled to be shown.

Now you need to insert a code component in the images routine, so that we can get the alternating images specified. Make sure the code component goes above the image components.

Put this in the “begin routine” tab:

next_image_loaded = False

# do different things on even and odd numbered trials:
    if imageLoop.thisN % 2 == 0:
        RSVPStream.opacity = 1    # show this one
        RSVPStream_2.opacity = 0  # hide this one
    else:
        RSVPStream.opacity = 0 
        RSVPStream_2.opacity = 1

Put this in the “every frame” tab, so this code will only run after the first frame has been shown (so that there is no delay in having the images switch over):

# only do this once, after the images switch, and not on the last trial:
if not next_image_loaded and frameN > 1 and imageLoop.thisN < 8:

    # find out the next image:
    next_trial = imageLoop.getFutureTrial()
    upcoming_image = next_trial['your_image_name_variable']

    # do different things on even and odd numbered trials:
    if imageLoop.thisN % 2 == 0:
        RSVPStream_2.image = upcoming_image
    else:
        RSVPStream.image = upcoming_image
    next_image_loaded = True

% is the modulo operator. It returns the remainder of dividing two numbers. So when dividing some number modulo 2, the remainder is 0 for even numbers, and 1 for odd numbers. This is very useful for code that needs to alternate across trials.

None of this is tested, so come back to us when you find the inevitable bug.

1 Like

hi @psych and @Michael

did you have any luck with this code?

i had a similar problem and tried the code out, but the images did not display continously in between routines so I am curious if setting the opacity takes time or there is something else that takes too much time here.

Would it work to precede the RSVP loop with a trial containing all of the images but off the screen. Potentially they could be moved and displayed in code during the RSVP loop using show/hide instead of redrawing

Usually the reason for avoiding that sort of approach is that you can run out of memory if there are lots of images, plus there is a long pause while all of the images are loaded at once. Ideally, if a scheme like the one above works, memory shouldn’t be an issue, as only two images are loaded at any time, and as they are each being loaded while another displays, there shouldn’t be any perceptible delays.

No, it is effectively instantaneous.

We are going to need much more detail here. It is not clear to me what this means.

Thanks for your replies.

This method ended up working for me when I uploaded to javascript/pavlovia. Running In the builder, however, there is some flickering in between routines where there is just a blank screen.